Procurando a documentação anterior?Acesse v1.docs.pluggy.ai
PluggyDocs

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

CampoObrigatórioObservações
summarysimTítulo curto. Nosso fluxo o reescreve como [SuaOrg/Instituição] problema (externalId), então seja descritivo mas não dependa dele.
descriptionsimO que está errado, desde quando, quantos usuários são afetados e como reproduzir. É o que a pessoa de engenharia lê primeiro.
itemIdsimUm 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.
productsimTransactions, Credit Card, Investments, Consent
problemTypesimA opção mais próxima do enum. Escolher bem é o que faz o chamado ser roteado corretamente.
externalIdaltamente recomendadoO 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.
accountTypenãoPessoa Física, Pessoa Jurídica, Corretora ou Outra. Diz em que tipo de conta devemos reproduzir.
accountIdnãoA conta da Pluggy em que o problema acontece, quando o item tem mais de uma. Evita uma ida e volta perguntando qual.
investmentIdnãoO mesmo para problemas de investimento: o investimento específico que está errado ou faltando.
prioritynãoDe 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.

  1. 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.
  2. 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.
  3. Validem a assinatura em toda requisição e respondam qualquer 2xx assim 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_changedo chamado mudou de status. previous traz o valor anterior.
  • ticket.resolvedo 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_addedrespondemos no chamado. Carrega o texto da resposta.
  • ticket.priority_changeda prioridade ou o SLA mudou. A primeira prioridade que observamos em um chamado não é uma mudança e não envia nada.
  • ticket.updatedo 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_added só 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 sequence diz 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/tickets lista os seus chamados com a data limite do SLA e se ele foi estourado, paginado por cursor (nextCursor na resposta, devolva-o como cursor), e POST /api/partners/tickets/{key}/comments adiciona 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 code e 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 429 com Retry-After. Faça backoff e tente de novo; não insista em looping.
  • Use dryRun durante a integração. Com "dryRun": true validamos 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

400Campos ausentes ou inválidos — a mensagem diz quais.
401Key ausente, desconhecida ou revogada.
403O item não pertence à sua conta.
404Item inexistente ou conexão excluída (ITEM_NOT_FOUND). Também para um item criado nos últimos minutos — tente de novo em instantes.
409O 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).
413Algum anexo passa de 10 MB.
429Cota mensal atingida. Respeite o Retry-After.
5xxProblema do nosso lado. Tente de novo com backoff exponencial; a chamada é idempotente quando você envia externalId.