{
  "openapi": "3.1.0",
  "info": {
    "title": "API Pix2DePix para comerciantes",
    "version": "1.0.0",
    "summary": "Cobranças Pix que liquidam em DePix na Liquid Network.",
    "description": "Gere cobranças Pix na sua plataforma e receba DePix.\n\n**O ponto que muda a sua integração:** existe um atraso antifraude entre o Pix ser pago e o DePix ser liquidado. Uma cobrança paga fica em `paid_pending_settlement` antes de chegar a `settled`. Libere produto em `settled`, não em `paid_pending_settlement`.\n\nTodos os valores são inteiros, em centavos de real.",
    "contact": {
      "name": "Suporte Pix2DePix",
      "url": "https://pix2depix.com"
    },
    "license": {
      "name": "Proprietária",
      "identifier": "LicenseRef-Proprietaria"
    }
  },
  "servers": [
    {
      "url": "https://api.pix2depix.com",
      "description": "Produção e sandbox — o que separa os dois é a chave"
    }
  ],
  "security": [
    {
      "chaveDeApi": []
    }
  ],
  "tags": [
    {
      "name": "Cobranças",
      "description": "Criar, consultar, listar e cancelar."
    },
    {
      "name": "Conta",
      "description": "Quem é você e qual taxa vale agora."
    },
    {
      "name": "Webhooks",
      "description": "Avisos automáticos no seu servidor."
    },
    {
      "name": "Sandbox",
      "description": "Só com chave `p2d_test_`: move a cobrança sem dinheiro."
    }
  ],
  "paths": {
    "/v1/charges": {
      "post": {
        "tags": [
          "Cobranças"
        ],
        "summary": "Cria uma cobrança",
        "description": "Devolve o QR Code e o copia-e-cola.\n\n**201** quando a cobrança nasce agora. **200** quando o mesmo `externalId` (ou a mesma `Idempotency-Key`) já tinha criado esta cobrança com o mesmo corpo — repetir a criação não duplica nada nem vira erro.",
        "operationId": "criarCobranca",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Retry seguro de rede. Se a resposta se perder, repita a requisição com a mesma chave: você recebe a cobrança original em vez de uma segunda.",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "example": "a3f1c2de-8f4b-4a1e-9c2b-0f1e2d3c4b5a"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CriarCobranca"
              },
              "examples": {
                "padrao": {
                  "summary": "Pedido de loja",
                  "value": {
                    "amountInCents": 25000,
                    "payerTaxNumber": "529.982.247-25",
                    "payerName": "Maria Silva",
                    "externalId": "pedido-8891",
                    "description": "Pedido #8891",
                    "metadata": {
                      "loja": "sp-01"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cobrança criada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cobranca"
                }
              }
            }
          },
          "200": {
            "description": "Cobrança já existia com este externalId e o mesmo corpo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cobranca"
                }
              }
            }
          },
          "400": {
            "description": "Corpo inválido (`invalid_request`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "402": {
            "description": "O provedor de Pix recusou (`provider_error`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "403": {
            "description": "Conta não liberada para cobrar (`merchant_not_activated`, `charge_blocked`, `missing_merchant_id`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "409": {
            "description": "`external_id_conflict` (mesmo externalId, corpo diferente), `idempotency_key_conflict` ou `charge_processing` (a original ainda está sendo criada — repita em 1s)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "429": {
            "description": "Limite por chave atingido (`rate_limited`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Cobranças"
        ],
        "summary": "Lista cobranças",
        "description": "Ordenadas da mais recente para a mais antiga. A paginação é por cursor: passe em `startingAfter` o `id` da última cobrança da página anterior.",
        "operationId": "listarCobrancas",
        "parameters": [
          {
            "name": "externalId",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "O id do pedido no seu sistema."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created",
                "awaiting_payment",
                "paid_pending_settlement",
                "settled",
                "expired",
                "canceled",
                "failed",
                "refunded"
              ]
            }
          },
          {
            "name": "createdAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "createdBefore",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "startingAfter",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`id` da última cobrança da página anterior."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de cobranças",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeCobrancas"
                }
              }
            }
          },
          "400": {
            "description": "Parâmetro inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "429": {
            "description": "Limite atingido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/v1/charges/{id}": {
      "get": {
        "tags": [
          "Cobranças"
        ],
        "summary": "Consulta uma cobrança",
        "description": "**Confirme por aqui antes de liberar produto.** O webhook é o aviso rápido; esta rota é a verdade.",
        "operationId": "consultarCobranca",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "O `id` que devolvemos na criação."
          }
        ],
        "responses": {
          "200": {
            "description": "A cobrança",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cobranca"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta e neste ambiente (`charge_not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "429": {
            "description": "Limite atingido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/v1/charges/{id}/cancel": {
      "post": {
        "tags": [
          "Cobranças"
        ],
        "summary": "Cancela uma cobrança não paga",
        "description": "Só antes do pagamento.\n\n⚠️ O QR Code continua tecnicamente pagável depois do cancelamento: nosso provedor de Pix não tem como invalidá-lo. Se o Pix cair mesmo assim, o pagamento vence e a cobrança volta a andar — você recebe `charge.paid_pending_settlement` como em qualquer outra. Tire o QR da frente do seu cliente ao cancelar.",
        "operationId": "cancelarCobranca",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cobrança cancelada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cobranca"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "404": {
            "description": "Não encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "409": {
            "description": "Já paga, expirada ou cancelada (`charge_not_cancelable`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "429": {
            "description": "Limite atingido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Dados do comerciante, plano e taxa vigente",
        "description": "A taxa vem do seu plano assinado. Se você trocar de plano, ela muda aqui e nas cobranças novas — não guarde percentual no seu código.",
        "operationId": "consultarConta",
        "responses": {
          "200": {
            "description": "A conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conta"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Dispara um evento de teste",
        "description": "Manda um `charge.test` para a URL configurada no painel, assinado igual a um evento de verdade. Serve para validar o seu código de conferência de assinatura.",
        "operationId": "testarWebhook",
        "responses": {
          "202": {
            "description": "Evento enfileirado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "eventId"
                  ],
                  "properties": {
                    "eventId": {
                      "type": "string",
                      "description": "O mesmo `id` que chegará no corpo do evento."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "404": {
            "description": "Nenhuma URL de webhook configurada para este ambiente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test/charges/{id}/advance": {
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Empurra uma cobrança de teste para o estado pedido",
        "description": "Existe só com chave `p2d_test_`. É como você exercita a espera da liquidação e os webhooks sem um Pix de verdade. As transições respeitam a mesma máquina de estados da produção: voltar atrás é `409 invalid_transition`.",
        "operationId": "avancarCobrancaDeTeste",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "paid_pending_settlement",
                      "settled",
                      "expired",
                      "refunded"
                    ]
                  }
                }
              },
              "examples": {
                "pagar": {
                  "summary": "Simular o Pix pago",
                  "value": {
                    "status": "paid_pending_settlement"
                  }
                },
                "liquidar": {
                  "summary": "Simular o DePix liquidado",
                  "value": {
                    "status": "settled"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A cobrança no novo estado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cobranca"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "403": {
            "description": "Chamada com chave de produção (`not_available_in_live`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "404": {
            "description": "Não encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          },
          "409": {
            "description": "A cobrança não pode ir para esse estado (`invalid_transition`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erro"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "cobranca": {
      "post": {
        "summary": "Evento de cobrança",
        "description": "Enviamos para a URL que você configurou no painel a cada mudança de estado.\n\nA entrega é **at-least-once**: o mesmo evento pode chegar mais de uma vez. Deduplique pelo campo `id`.\n\nResponda **2xx** rápido. Qualquer outra coisa — inclusive 3xx — conta como falha e entra na fila de retentativa, que insiste por até 24h com espera crescente.",
        "operationId": "eventoDeCobranca",
        "parameters": [
          {
            "name": "X-P2D-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Id do evento. É por ele que você deduplica."
          },
          {
            "name": "X-P2D-Event-Type",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-P2D-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Epoch em segundos. Recuse o que estiver a mais de 300s do seu relógio."
          },
          {
            "name": "X-P2D-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^v1=[0-9a-f]{64}$"
            },
            "description": "`v1=` + HMAC-SHA256 de `<timestamp>.<corpo bruto>` com o segredo do webhook, em hex."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Evento"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido. Qualquer 2xx serve."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "chaveDeApi": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer p2d_live_...` em produção, `p2d_test_...` em sandbox. Gere no painel, em **API**. O segredo aparece uma única vez."
      }
    },
    "schemas": {
      "CriarCobranca": {
        "type": "object",
        "required": [
          "amountInCents",
          "payerTaxNumber"
        ],
        "properties": {
          "amountInCents": {
            "type": "integer",
            "format": "int64",
            "description": "Valor bruto em centavos. Mínimo 1000 (R$ 10,00), máximo 500000 (R$ 5.000,00).",
            "example": 25000
          },
          "payerTaxNumber": {
            "type": "string",
            "description": "CPF ou CNPJ de quem vai pagar. Com ou sem pontuação. Obrigatório: é o provedor de Pix que exige, para identificar o pagador.",
            "example": "529.982.247-25"
          },
          "payerName": {
            "type": "string",
            "maxLength": 120,
            "description": "Nome de quem vai pagar. Não é conferido — depois do pagamento devolvemos o nome que o banco informou."
          },
          "externalId": {
            "type": "string",
            "maxLength": 128,
            "description": "O id do pedido no seu sistema. Opcional, mas é o que torna a criação idempotente: o mesmo valor nunca vira duas cobranças.",
            "example": "pedido-8891"
          },
          "description": {
            "type": "string",
            "maxLength": 200,
            "description": "Some na resposta e nos eventos. Não vai para o extrato do pagador."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 20,
            "description": "Pares livres, devolvidos em toda resposta e em todo evento."
          }
        }
      },
      "Cobranca": {
        "type": "object",
        "required": [
          "id",
          "status",
          "environment",
          "amount",
          "pix",
          "settlement",
          "payer",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Nosso id. 24 caracteres hexadecimais.",
            "example": "6a92586d486e6a4f3d305214"
          },
          "externalId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "awaiting_payment",
              "paid_pending_settlement",
              "settled",
              "expired",
              "canceled",
              "failed",
              "refunded"
            ],
            "description": "Veja a tabela de status."
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/Valores"
          },
          "pix": {
            "$ref": "#/components/schemas/Pix"
          },
          "settlement": {
            "$ref": "#/components/schemas/Liquidacao"
          },
          "payer": {
            "type": "object",
            "required": [
              "taxNumber"
            ],
            "properties": {
              "taxNumber": {
                "type": "string",
                "description": "Mascarado. Serve para você reconhecer quem pagou, não para guardar.",
                "example": "•••.982.247-••"
              },
              "name": {
                "type": "string",
                "description": "Depois do pagamento, o nome que o banco informou."
              }
            }
          },
          "description": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Valores": {
        "type": "object",
        "required": [
          "grossInCents",
          "feeBps",
          "feeFixedInCents",
          "feeInCents",
          "netInCents"
        ],
        "description": "A taxa é uma parcela fixa por depósito mais um percentual do seu plano.",
        "properties": {
          "grossInCents": {
            "type": "integer",
            "format": "int64",
            "description": "O que o seu cliente paga no Pix.",
            "example": 25000
          },
          "feeBps": {
            "type": "integer",
            "description": "Percentual do plano em pontos-base. 199 = 1,99%.",
            "example": 199
          },
          "feeFixedInCents": {
            "type": "integer",
            "format": "int64",
            "description": "Parcela fixa por depósito.",
            "example": 99
          },
          "feeInCents": {
            "type": "integer",
            "format": "int64",
            "description": "Fixa + percentual.",
            "example": 597
          },
          "netInCents": {
            "type": "integer",
            "format": "int64",
            "description": "O que chega em DePix na sua carteira.",
            "example": 24403
          }
        }
      },
      "Pix": {
        "type": "object",
        "properties": {
          "qrCode": {
            "type": "string",
            "description": "Copia-e-cola. É isto que vira QR Code na sua tela."
          },
          "qrCodeImageUrl": {
            "type": "string",
            "format": "uri",
            "description": "Imagem pronta do QR. Pode não vir — gere o QR do `qrCode` se preferir."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Passou disso, a cobrança vira `expired`."
          }
        }
      },
      "Liquidacao": {
        "type": "object",
        "description": "O caminho do dinheiro depois do pagamento.",
        "properties": {
          "delayHours": {
            "type": "integer",
            "description": "Retenção antifraude entre o Pix pago e o DePix liquidado.",
            "example": 24
          },
          "estimatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o DePix deve sair. Só existe depois do pagamento — é a data que você mostra ao seu cliente."
          },
          "paidAt": {
            "type": "string",
            "format": "date-time"
          },
          "settledAt": {
            "type": "string",
            "format": "date-time"
          },
          "blockchainTxId": {
            "type": "string",
            "description": "Comprovante on-chain na Liquid. Só depois de `settled`."
          }
        }
      },
      "ListaDeCobrancas": {
        "type": "object",
        "required": [
          "data",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Cobranca"
            }
          },
          "hasMore": {
            "type": "boolean",
            "description": "Se `true`, peça a próxima página com `startingAfter` no `id` do último item."
          }
        }
      },
      "Conta": {
        "type": "object",
        "required": [
          "merchantId",
          "email",
          "environment",
          "plan",
          "feeFixedInCents",
          "limits",
          "settlementDelayHours"
        ],
        "properties": {
          "merchantId": {
            "type": "string",
            "description": "Seu identificador no provedor de Pix."
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "O ambiente da chave que você usou."
          },
          "plan": {
            "type": "object",
            "required": [
              "id",
              "name",
              "feeBps"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "feeBps": {
                "type": "integer",
                "description": "Sua taxa percentual em pontos-base."
              }
            }
          },
          "feeFixedInCents": {
            "type": "integer",
            "format": "int64",
            "description": "Parcela fixa por depósito.",
            "example": 99
          },
          "limits": {
            "type": "object",
            "properties": {
              "minChargeInCents": {
                "type": "integer",
                "format": "int64",
                "description": "Mínimo por cobrança.",
                "example": 1000
              },
              "maxChargeInCents": {
                "type": "integer",
                "format": "int64",
                "description": "Máximo por cobrança.",
                "example": 500000
              }
            }
          },
          "settlementDelayHours": {
            "type": "integer",
            "description": "Retenção antifraude aplicada às cobranças.",
            "example": 24
          },
          "webhook": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri"
              },
              "active": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "Evento": {
        "type": "object",
        "required": [
          "id",
          "type",
          "createdAt",
          "environment",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Imutável. Deduplique por ele."
          },
          "type": {
            "type": "string",
            "enum": [
              "charge.awaiting_payment",
              "charge.paid_pending_settlement",
              "charge.settled",
              "charge.expired",
              "charge.canceled",
              "charge.failed",
              "charge.refunded",
              "charge.test"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "data": {
            "type": "object",
            "required": [
              "charge"
            ],
            "properties": {
              "charge": {
                "$ref": "#/components/schemas/Cobranca"
              }
            }
          }
        }
      },
      "Erro": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "O formato é o mesmo em todos os endpoints e em todos os códigos HTTP.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "requestId"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "invalid_api_key",
                  "revoked_api_key",
                  "merchant_not_activated",
                  "charge_blocked",
                  "missing_merchant_id",
                  "charge_not_found",
                  "external_id_conflict",
                  "charge_processing",
                  "charge_not_cancelable",
                  "invalid_transition",
                  "idempotency_key_conflict",
                  "rate_limited",
                  "provider_error",
                  "provider_unavailable",
                  "not_available_in_live",
                  "internal_error"
                ],
                "description": "Legível por máquina. É nele que você escreve o `if` — a mensagem pode mudar."
              },
              "message": {
                "type": "string",
                "description": "Texto em português, para humano ler."
              },
              "details": {
                "type": "array",
                "description": "Só em erro de validação: um item por campo recusado.",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              },
              "requestId": {
                "type": "string",
                "description": "Mande este id ao suporte: é por ele que achamos a requisição."
              }
            }
          }
        }
      }
    }
  }
}
