API de Suporte para Parceiros
Abra e acompanhe chamados de suporte da Pluggy a partir dos seus próprios sistemas. Um chamado criado por esta API é idêntico a um aberto manualmente no portal de suporte: mesmo tipo de solicitação, mesmo formulário, mesmo SLA e mesma fila. Seu time continua vendo tudo no portal, como sempre.
Contrato legível por máquina: /api/partners/docs (OpenAPI 3.0 — aponte seu gerador de código para ele).
Autenticação
Envie a API key que seu contato na Pluggy gerou para você:
Authorization: Bearer pk_live_…
# or
X-Api-Key: pk_live_…A key identifica a sua conta — você nunca envia id de time ou de cliente, e uma key só consegue abrir chamados da própria conta. Trate-a como segredo; se vazar, peça a revogação e emitimos uma nova na hora.
Abrir um chamado
curl -X POST https://docs.pluggy.ai/api/partners/tickets \
-H "Authorization: Bearer $PLUGGY_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{
"summary": "Transação do cartão de crédito não sendo retornada",
"description": "Item conectado e atualizado, fatura fechada em 05/08. As 3 últimas transações não aparecem na API. Afeta ~40 usuários desde 04/08.",
"itemId": "8f2a1b3c-0000-4d4e-9a9a-111122223333",
"product": "Credit Card",
"problemType": "Transação do cartão de credito não sendo retornada",
"accountType": "Pessoa Física",
"externalId": "YOUR-1947",
"priority": "P1 — Alto"
}'Resposta:
{
"ticket": "SUP2-13826",
"portalUrl": "https://pluggy.atlassian.net/servicedesk/customer/portal/1/SUP2-13826",
"status": "Aberto",
"created": true,
"deduplicatedFrom": null,
"attachmentsUploaded": 0,
"derivedFromItem": { "connectorName": "Nubank", "isOpenFinance": true },
"unmappedValues": []
}Campos
| Campo | Obrigatório | Observações |
|---|---|---|
| summary | sim | Título curto. Nosso fluxo o reescreve como [SuaOrg/Instituição] problema (externalId), então seja descritivo mas não dependa dele. |
| description | sim | O que está errado, desde quando, quantos usuários são afetados e como reproduzir. É o que a pessoa de engenharia lê primeiro. |
| itemId | sim | Um item da Pluggy onde o problema se reproduz. Precisa pertencer à sua conta, e é o conector dele que define a instituição — você não envia nenhuma. Um chamado citando o item de outra conta é recusado antes de ser criado. |
| product | sim | Transactions, Credit Card, Investments, Consent… |
| problemType | sim | A opção mais próxima do enum. Escolher bem é o que faz o chamado ser roteado corretamente. |
| externalId | altamente recomendado | O id do seu próprio chamado. Torna a chamada idempotente — uma nova tentativa devolve o chamado existente em vez de duplicar — e aparece no título, para os dois lados se referenciarem. |
| accountType | não | Pessoa Física, Pessoa Jurídica, Corretora ou Outra. Diz em que tipo de conta devemos reproduzir. |
| accountId | não | A conta da Pluggy em que o problema acontece, quando o item tem mais de uma. Evita uma ida e volta perguntando qual. |
| investmentId | não | O mesmo para problemas de investimento: o investimento específico que está errado ou faltando. |
| priority | não | De P0 — Crítico a P3 — Baixo. A sua leitura do impacto — é um sinal para a triagem, não um compromisso de SLA. |
Evidências e anexos
Prints, arquivos HAR e logs são a diferença entre um chamado resolvido em um dia e outro que fica indo e voltando por uma semana. Envie até 5 arquivos por chamado, de 10 MB cada, como URL HTTPS que buscamos uma única vez (preferido — URLs assinadas com TTL curto funcionam bem) ou em base64 inline, para arquivos pequenos.
"attachments": [
{ "filename": "evidencia.har", "contentType": "application/json",
"url": "https://files.example.com/signed/evidencia.har" },
{ "filename": "log.txt", "contentType": "text/plain",
"base64": "TG9nIGNvbnRlbnQ=" }
]Consultar status
curl https://docs.pluggy.ai/api/partners/tickets/SUP2-13826 \
-H "Authorization: Bearer $PLUGGY_PARTNER_KEY"Retorna o status atual e os relógios de SLA. Você só consegue ler chamados que pertencem à sua conta.
Eventos de chamado (webhooks)
Em vez de consultar a API atrás de mudanças, nos passe um endpoint HTTPS e enviaremos um POST com o evento sempre que um chamado de vocês andar. A API continua disponível para todo o resto — abrir chamados, responder e reconciliar quando quiserem ter certeza — então o webhook é o caminho rápido, não o único.
- Envie a URL HTTPS onde querem receber os eventos, pelo contato de sempre na Pluggy. Uma URL separada para homologação funciona; só nos digam qual é qual.
- Respondemos com um segredo de assinatura (
whsec_…). Ele aparece uma única vez e não pode ser lido depois — guardem junto das outras credenciais de vocês. Se for perdido, peçam a rotação. - Validem a assinatura em toda requisição e respondam qualquer
2xxassim que tiverem aceitado o evento de forma durável. Enfileirem e processem de forma assíncrona: esperamos 8 segundos pela resposta, e qualquer coisa mais lenta conta como entrega falha.
São cinco eventos, todos derivados da mesma comparação com o sistema de chamados que mantém a nossa cópia do chamado de vocês correta — por isso um evento nunca afirma uma mudança que não aconteceu:
ticket.status_changed— o chamado mudou de status.previoustraz o valor anterior.ticket.resolved— o chamado foi resolvido. Enviado no lugar da mudança de status, não junto com ela, então uma mudança real é um evento.ticket.message_added— respondemos no chamado. Carrega o texto da resposta.ticket.priority_changed— a prioridade ou o SLA mudou. A primeira prioridade que observamos em um chamado não é uma mudança e não envia nada.ticket.updated— o título mudou.
{
"eventId": "0f2b6c4e-7a1d-4a2e-9c3f-5b8d1e0a7c44",
"sequence": 1042,
"event": "ticket.message_added",
"occurredAt": "2026-09-18T13:42:07.912Z",
"ticket": {
"key": "SUP2-13826",
"number": 418,
"title": "Transação do cartão de crédito não sendo retornada",
"state": "on_customer",
"status": "On customer",
"updatedAt": "2026-09-18T13:41:58.000Z",
"portalUrl": "https://pluggy.atlassian.net/servicedesk/customer/portal/1/SUP2-13826"
},
"message": {
"author": "Pluggy Support",
"text": "Confirmamos com a instituição: as transações entram no próximo ciclo.",
"sentAt": "2026-09-18T13:41:58.000Z"
}
}Toda requisição leva `X-Pluggy-Signature: t=<unix segundos>,v1=<hmac hex>`. O que é assinado é a string `"{t}.{corpo bruto}"` — o corpo exatamente como chegou, antes de qualquer parse de JSON, porque serializar de novo o objeto já interpretado muda os bytes e a assinatura não confere. O `t` também é o mecanismo anti-replay: rejeitem o que tiver mais de cinco minutos.
import { createHmac, timingSafeEqual } from "node:crypto";
// `raw` is the body exactly as it arrived — parse it only after this passes.
export function verify(raw: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("=", 2) as [string, string]),
);
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!Number.isFinite(age) || age > 300) return false; // anti-replay
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${raw}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}- Notas internas nunca saem. Nossos chamados têm notas internas ao lado das respostas que vocês veem. O
ticket.message_addedsó carrega mensagem visível para vocês no portal — nunca uma nota interna. - Deduplique pelo `eventId`. Ele é estável entre reenvios e também vai no cabeçalho
X-Pluggy-Event-Id. O mesmo id chegando duas vezes significa a mesma mudança, não duas. - A ordem não é garantida. O
sequencediz qual evento foi criado primeiro, para vocês descartarem um que já foi superado — mas não é a ordem de entrega. Tratem o evento como um aviso para ler o chamado, não como o estado em si. - Reenviamos. Seis tentativas com backoff — 30s, 2m, 10m, 1h e 6h — e depois disso a entrega é marcada como falha e podemos reenviar manualmente com o mesmo
eventId. Esses intervalos são quando a tentativa fica devida, não uma promessa de quando ela chega.
Regras do jogo
- Um chamado por problema, não por usuário final. Se o mesmo bug de conector afeta 500 dos seus usuários, isso é um chamado com o volume descrito — não 500 chamados. É o fator que mais pesa na velocidade da resposta.
- Acompanhar e responder
GET /api/partners/ticketslista os seus chamados com a data limite do SLA e se ele foi estourado, paginado por cursor (nextCursorna resposta, devolva-o comocursor), ePOST /api/partners/tickets/{key}/commentsadiciona um comentário público — o mesmo que o seu time escreveria no portal, então o suporte o vê na conversa e não em uma nota interna. - Você não envia mais a instituição. Nós a lemos a partir do itemId, junto com o fato de o conector ser ou não Open Finance. Se o conector não estiver entre as opções do nosso formulário, o chamado é registrado como “Outra” com o nome real na descrição — nada se perde.
- Antes de abrir o chamado, validamos o item Quatro situações impedem a criação do chamado — conexão excluída, sem atualização há mais de duas semanas, só execuções com credenciais inválidas, ou consentimento Open Finance aguardando os demais administradores. Em todas, a correção está na conexão e o suporte só poderia responder o mesmo. A resposta traz um
codee uma mensagem escrita para o seu usuário final (ver a tabela de erros abaixo). - Sempre envie externalId. Retentativas e automações que disparam duas vezes são normais; idempotência é o que impede que virem duplicatas.
- Cota. Cada key tem uma cota mensal de chamados. Ao estourá-la, a API responde
429comRetry-After. Faça backoff e tente de novo; não insista em looping. - Use dryRun durante a integração. Com
"dryRun": truevalidamos o payload e devolvemos o que seria registrado, sem criar nada e sem consumir cota. - O que não entra aqui: incidentes em andamento (acompanhe a status page), dúvidas de integração (assistente da documentação) e pedidos de funcionalidade (fale com seu contato na Pluggy).
Erros
| 400 | Campos ausentes ou inválidos — a mensagem diz quais. |
| 401 | Key ausente, desconhecida ou revogada. |
| 403 | O item não pertence à sua conta. |
| 404 | Item inexistente ou conexão excluída (ITEM_NOT_FOUND). Também para um item criado nos últimos minutos — tente de novo em instantes. |
| 409 | O item não pode ser investigado como está: ITEM_TOO_OLD (atualize a conexão), ITEM_ONLY_HAS_INVALID_CREDENTIALS_EXECUTIONS (o usuário final precisa reconectar) ou OF_ITEM_RESOURCES_WITHOUT_MASTER_PERMISSIONS (faltam os demais administradores autorizarem). |
| 413 | Algum anexo passa de 10 MB. |
| 429 | Cota mensal atingida. Respeite o Retry-After. |
| 5xx | Problema do nosso lado. Tente de novo com backoff exponencial; a chamada é idempotente quando você envia externalId. |
