Idempotência
As tools que criam um recurso ou movem dinheiro aceitam um argumento opcional idempotency_key. Ele é enviado ao endpoint como o cabeçalho X-Idempotency-Key que a API REST já honra.
{ "name": "billing_invoices_create",
"arguments": {
"billing_account_id": "…",
"total": 1250.00,
"idempotency_key": "7c4a8d09-ca37-4f21-b1f0-3d2f2b8c9a11"
} }
| Formato | String opaca, 1 a 255 caracteres. Um UUID é a escolha usual. |
| Repetição com o mesmo corpo | Devolve a resposta original, sem rodar a operação de novo. |
| Repetição com corpo diferente | Conflito. |
Por que um agente precisa disto mais que o seu backend
Um cliente MCP que dá timeout e tenta de novo, ou um modelo que decide "não tenho certeza se aquilo funcionou, vou refazer", é exatamente o padrão que cria a segunda fatura, a segunda cobrança e o segundo cliente. Seu backend tem um laço de retry que você escreveu; o agente tem um julgamento.
Mande uma chave sempre que a operação cobre, emita ou crie. Leituras e atualizações são naturalmente idempotentes e não pedem chave.
Tools que aceitam
| Família | Tools |
|---|---|
customers | billing_customers_create, billing_customers_upsert, billing_billing_accounts_create, billing_billing_accounts_upsert |
portal-users | billing_customer_users_create, billing_customer_users_invite |
users, roles | billing_dashboard_users_create, billing_dashboard_users_upsert, billing_team_roles_create, billing_team_roles_upsert |
products, plans, services | billing_products_create, billing_products_upsert, billing_product_groups_create, billing_product_groups_upsert, billing_plans_create, billing_plans_upsert, billing_plan_groups_create, billing_plan_groups_upsert, billing_service_items_create, billing_service_items_upsert |
subscriptions, plan-changes | billing_subscriptions_create, billing_subscriptions_upsert, billing_subscriptions_items_change, billing_plan_changes_execute, billing_plan_changes_rules_create |
invoices | billing_invoices_create, billing_invoices_upsert, billing_invoices_finalize, billing_invoices_pay, billing_invoices_installments_pay, billing_subscriptions_create_invoice |
payments | billing_payments_create, billing_payments_retry, billing_payments_refund, billing_payment_methods_create, billing_payment_methods_upsert |
credits, coupons | billing_credits_create, billing_credits_upsert, billing_coupons_create, billing_coupons_upsert |
nfes, companies | billing_nfes_create, billing_nfes_upsert, billing_nfes_issue, billing_companies_create, billing_companies_upsert |
tax-rules, tax-periods, withholdings | billing_tax_rules_create, billing_tax_rules_upsert, billing_tax_periods_process_month, billing_withholdings_create, billing_withholdings_upsert, billing_withholdings_convert_to_credit, billing_withholdings_convert_to_credits |
proposals, templates | billing_proposals_create, billing_proposals_upsert, billing_proposals_send, billing_proposals_accept, billing_proposal_templates_create, billing_proposal_templates_upsert |
usage | billing_usage_report, billing_usage_snapshots_daily_create, billing_usage_snapshots_monthly_create |
:::caution As que cobram
billing_invoices_pay, billing_payments_create, billing_payments_retry e billing_nfes_issue produzem um efeito no mundo — uma cobrança no gateway, uma nota fiscal na SEFAZ — que nenhum _delete desfaz. Mande idempotency_key nessas mesmo quando você acha que não vai repetir.
:::
Upsert não é idempotência
*_upsert casa um registro existente por id, depois external_id, depois pela chave de negócio do recurso, e cria só se não achar. Isso resolve "não sei se já existe", que é um problema diferente de "não sei se minha chamada chegou". As duas coisas se combinam bem: um upsert com idempotency_key é seguro nas duas dimensões.