ReporteiDevelopers
Referência da APIConnect Explorer
Navegação
Recursos da API
Materiais Complementares
Connect ExplorerPostman CollectionVídeo Guia
Connect API v1

Bem-vindo à API do Reportei Connect

O Reportei Connect é um conector de dados: ele coloca dentro do seu software as mais de 50 integrações que o Reportei já mantém com redes sociais, plataformas de anúncios e ferramentas de marketing. Em vez de implementar e manter um conector para cada plataforma, você acessa todas por uma única API, com autenticação, coleta e renovação de tokens resolvidas pelo Connect.

O modelo é direto: o seu merchant cria customers, cada customer conecta integrações através de uma sessão, e as métricas são consultadas a partir de cada integração conectada.

Para gerar o seu token de merchant, é preciso ter uma conta no Reportei Connect com o trial iniciado.

Iniciar trial no Reportei Connect

Base URL

https://connect.reportei.com/api

Autenticação

A API usa dois tokens: o Bearer Token do merchant, presente em todas as chamadas, e o customer token, exigido nos endpoints que acessam dados de um cliente específico.

Bearer Token (Merchant)

Para autenticar requisições relacionadas à sua conta de merchant, inclua seu token de acesso no cabeçalho Authorization.

Authorization: Bearer YOUR_ACCESS_TOKEN

Customer Token

Para acessar dados de um cliente específico, use o token gerado para ele. Este token deve ser enviado no cabeçalho x-customer-token, juntamente com o Bearer Token.

x-customer-token: CUSTOMER_API_TOKEN

Importante

O customer token é gerado apenas uma vez na criação do cliente. Certifique-se de armazená-lo com segurança, pois ele não pode ser recuperado posteriormente.

Sem x-customer-token, a resposta é 401

Uma rota que acessa dados de um cliente exige o x-customer-token. Sem ele, ou com um token inválido, ela responde 401 e nunca devolve os dados de todos os seus clientes. A visão de todos os clientes fica nas rotas de panorama em /merchants/*, que pedem só o Bearer.

Paginação, Respostas e Erros

Convenções que valem para todos os endpoints da API: como paginar listas, qual é a estrutura das respostas e como interpretar os códigos de status.

Paginação

Endpoints que retornam listas de recursos suportam paginação através dos parâmetros page e per_page.

NomeDescrição
pageNúmero da página (padrão: 1)
per_pageItens por página (padrão: 15, máximo: 100)

Requisição

curl -X GET "https://connect.reportei.com/api/customers?page=2&per_page=20" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Resposta

{
  "data": [...],
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total": 150,
    "total_pages": 8
  }
}

Formato de Respostas

Todas as respostas da API seguem um formato JSON consistente para facilitar o processamento dos dados.

Resposta de sucesso

{
  "data": {
    "id": "uuid",
    "type": "resource_type",
    "attributes": {
      // Resource attributes
    }
  }
}

Resposta de lista

{
  "data": [
    {
      "id": "uuid",
      "type": "resource_type",
      "attributes": { ... }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 100
  }
}

Resposta de erro

{
  "error": {
    "code": "error_code",
    "message": "Human readable error message",
    "details": {
      // Additional error information
    }
  }
}

Tratamento de Erros

A API utiliza códigos de status HTTP padrão para indicar o sucesso ou falha de uma requisição.

200 OK

Requisição bem-sucedida

201 Created

Recurso criado com sucesso

400 Bad Request

Requisição inválida ou malformada

401 Unauthorized

Token de autenticação ausente ou inválido

403 Forbidden

Sem permissão para acessar o recurso

404 Not Found

Recurso não encontrado

422 Unprocessable Entity

Validação falhou nos dados enviados

429 Too Many Requests

Limite de taxa excedido

500 Internal Server Error

Erro no servidor

Exemplo de erro

{
  "error": {
    "code": "validation_error",
    "message": "The given data was invalid",
    "details": {
      "email": ["The email field is required"],
      "name": ["The name must be at least 3 characters"]
    }
  }
}

Merchants

Consulte as configurações do seu merchant, os totais e o panorama da sua conta, as integrações liberadas no seu plano e o catálogo completo de plataformas do Connect.

Panorama da conta

As rotas em /merchants/* leem o que o Connect guarda de todos os seus clientes, só com o Bearer e sem x-customer-token, para você acompanhar o que foi feito pela API: os totais da conta, as campanhas, os conjuntos, os anúncios, os posts, as mídias e as execuções de tools. Nessas rotas, customer, customer_integration e customer_toolkit são filtros opcionais, nunca credencial: um uuid que não é seu responde 403.

GET/merchants/settings

Settings

Retorna informações sobre o seu merchant, incluindo limites de taxa (rate limiting) e a lista de integrações disponíveis para conexão.

{
  "merchant": {
    "uuid": "c1e6c85a-6441-4107-ab36-b8bf581b40e0",
    "name": "Reportei",
    "app_url": "https://reportei.com",
    "redirect_url": "https://reportei.com",
    "created_at": "2024-09-24 17:07:26",
    "updated_at": "2024-09-24 17:07:26",
    "rate_limit_per_minute": "999",
    "rate_limit_per_second": "999",
    "total_customers": 2,
    "available_integrations": [
      {
        "name": "Instagram Business",
        "slug": "instagram_business"
      },
      {
        "name": "Facebook",
        "slug": "facebook"
      }
    ]
  }
}
GET/merchants/stats

Totais da conta

Quantos clientes, conexões, posts e anúncios a sua conta tem no Connect, contados na hora do pedido, sem período. posts.total conta os posts pedidos pela API em qualquer status. ads.total conta os anúncios que o Connect criou, apagados incluídos; ads.failed conta as criações que falharam (POST /ads, /ads/boost e /ads/{id}/duplicate), que não existem na conta e por isso ficam fora do total.

{
  "data": {
    "customers": 1,
    "customer_integrations": 3,
    "posts": {
      "total": 12,
      "published": 9,
      "failed": 1
    },
    "ads": {
      "total": 7,
      "failed": 1
    }
  }
}
GET/merchants/ads/campaigns

Campanhas de todos os clientes

As campanhas que o Connect guarda, de todos os seus clientes, como no GET /ads/campaigns com source=connect. Filtros customer, customer_integration, status, effective_status e include_deleted; paginação por page e per_page. A resposta mostra o último estado conhecido (synced_at); refresh=true relê a conta e exige customer_integration, a conexão pela qual ela é lida.

{
  "data": [
    {
      "id": "120250000000000001",
      "uuid": "9a1d3f52-7c1e-4c55-9b0e-5c3f1c6a2e10",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "name": "Black Friday",
      "status": "paused",
      "effective_status": "paused",
      "issues": [],
      "budget": {
        "amount": 50,
        "type": "daily",
        "level": "campaign",
        "currency": "BRL"
      },
      "adsets_count": 1,
      "facebook_ads": {
        "objective": "traffic",
        "spend_cap": null,
        "bid_strategy": "lowest_cost"
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/merchants/ads/campaigns?page=1",
    "last": "https://connect.reportei.com/api/merchants/ads/campaigns?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/ads/campaigns",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/merchants/ads/adsets

Conjuntos de todos os clientes

Os conjuntos de todos os seus clientes. Mesmos filtros e refresh das campanhas, mais campaign_id (o id da campanha ou o uuid).

{
  "data": [
    {
      "id": "120250000000000002",
      "uuid": "1b2c3d4e-5f60-4718-8a9b-0c1d2e3f4a5b",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "campaign_id": "120250000000000001",
      "name": "BR 25-45",
      "status": "paused",
      "effective_status": "paused",
      "issues": [],
      "budget": null,
      "schedule": {
        "start": "2026-11-20T00:00:00-0300",
        "end": null
      },
      "targeting": {
        "countries": [
          "BR"
        ],
        "age_min": 25,
        "age_max": 45,
        "genders": [
          "all"
        ]
      },
      "facebook_ads": {
        "optimization_goal": "link_clicks",
        "billing_event": "impressions",
        "bid_strategy": null,
        "bid_amount": null,
        "placements": {},
        "targeting": {
          "interests": [],
          "custom_audiences": [],
          "excluded_custom_audiences": [],
          "advantage_audience": false,
          "raw": {}
        }
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/merchants/ads/adsets?page=1",
    "last": "https://connect.reportei.com/api/merchants/ads/adsets?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/ads/adsets",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/merchants/ads

Anúncios de todos os clientes

Os anúncios de todos os seus clientes. Mesmos filtros e refresh das campanhas, mais campaign_id e adset_id, que aceitam id ou uuid. effective_status=disapproved traz os reprovados de todas as contas, com o motivo em issues.

{
  "data": [
    {
      "id": "120250000000000003",
      "uuid": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "campaign_id": "120250000000000001",
      "adset_id": "120250000000000002",
      "name": "Black Friday, imagem",
      "status": "active",
      "effective_status": "pending_review",
      "issues": [],
      "format": "image",
      "headline": "Black Friday",
      "body": "Até 50% off",
      "description": null,
      "link_url": "https://loja.com/bf",
      "call_to_action": "shop_now",
      "thumbnail_url": null,
      "facebook_ads": {
        "creative_id": "120250000000000004",
        "page_id": "1068595869664481",
        "instagram_account_id": null,
        "post_id": null
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/merchants/ads?page=1",
    "last": "https://connect.reportei.com/api/merchants/ads?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/ads",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/merchants/posts

Posts de todos os clientes

Os posts pedidos pela API, de todos os seus clientes, do mais recente para o mais antigo, com o customer_uuid de cada um. Filtros customer, customer_integration, status (created, publishing, published ou failed) e external_reference; per_page de 1 a 100, padrão 15.

{
  "data": [
    {
      "uuid": "9f1c2b7e-1d3a-4c55-9a8e-2f6b0c1d4e5f",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "external_reference": "post-2026-10-08-001",
      "integration_slug": "instagram_business",
      "source_id": "17841400000000000",
      "status": "published",
      "publishing": { "status": "published", "external_id": "18393642439204130", "error": null, "published_at": "2026-10-08 14:00:42" },
      "...": "..."
    },
    {
      "uuid": "1b2e7c90-55aa-4f0e-8d0c-3a9e6f7d2c11",
      "customer_uuid": "6c1f0a9e-2b3d-4e5f-9a8b-7c6d5e4f3a2b",
      "external_reference": "fb-post-2026-10-09-001",
      "integration_slug": "facebook",
      "source_id": "104512345678901",
      "status": "failed",
      "publishing": {
        "status": "failed",
        "external_id": null,
        "error": { "code": "324", "message": "Missing or invalid image file." },
        "published_at": null
      },
      "...": "..."
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/merchants/posts?page=1",
    "last": "https://connect.reportei.com/api/merchants/posts?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/posts",
    "per_page": 15,
    "to": 2,
    "total": 2
  }
}
GET/merchants/tools/executions

Execuções de tools de todos os clientes

As execuções de POST /tools/{tool_slug}/execute de todos os seus clientes, da mais recente para a mais antiga, cada uma com o seu customer_uuid. Filtros customer, customer_toolkit, toolkit_slug e tool_slug; per_page de 1 a 100, padrão 15. Um customer ou customer_toolkit que não é seu, ou uma conexão que não é do customer informado, responde 403.

{
  "data": [
    {
      "uuid": "0d3c8b4a-2e1f-4c6d-9a7b-5e4f3a2b1c0d",
      "tool_slug": "jira_get_issue",
      "toolkit_slug": "jira",
      "customer_toolkit": "7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "method": "GET",
      "url": "https://acme.atlassian.net/rest/api/3/issue/KEY-123",
      "payload": {
        "issueIdOrKey": "KEY-123",
        "fields": "summary,status"
      },
      "http_status": 200,
      "response": {
        "id": "10001",
        "key": "KEY-123",
        "fields": {
          "summary": "Fix the login",
          "status": {
            "name": "In Progress"
          }
        }
      },
      "error_message": null,
      "duration_ms": 412,
      "created_at": "2026-10-19T12:00:00.000000Z"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/merchants/tools/executions?toolkit_slug=jira&page=1&per_page=15",
    "last": "https://connect.reportei.com/api/merchants/tools/executions?toolkit_slug=jira&page=1&per_page=15",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/tools/executions",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/merchants/media

Mídias de todos os clientes

As mídias que ainda valem, de todos os seus clientes, da mais recente para a mais antiga, cada uma com o seu customer_uuid. Filtros customer e status (uploading ou ready); per_page de 1 a 100, padrão 15. Um customer que não é seu responde 403.

{
  "data": [
    {
      "uuid": "8b1e4f2a-6c3d-4e5f-9a0b-1c2d3e4f5a6b",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "status": "ready",
      "type": "image",
      "mime_type": "image/jpeg",
      "name": "verao-4x5.jpg",
      "size_bytes": 482133,
      "url": "https://file.connect.reportei.com/media/2f0c.../8b1e....jpg",
      "expires_at": "2026-11-29T12:00:00.000000Z",
      "upload": null,
      "created_at": "2026-10-30T12:00:00.000000Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/merchants/media",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/integrations

Listar Plataformas

Retorna o catálogo completo de plataformas que o Connect coleta, liberadas ou não no seu plano. O slug de cada uma é o que GET /metrics/{integration_slug} e as sessões de integração recebem.

{
  "data": [
    {
      "id": 1,
      "name": "Instagram Business",
      "slug": "instagram_business",
      "type": 1,
      "color": "#FC4ADE",
      "icon": { "name": "social.instagram_business", "color": "pink-600" },
      "enabled": true,
      "created_at": "2024-09-13 14:12:56",
      "updated_at": "2024-09-13 14:12:56"
    },
    {
      "id": 3,
      "name": "Meta Ads",
      "slug": "facebook_ads",
      "type": 3,
      "color": "#0668E1",
      "icon": { "name": "social.meta_ads", "color": "indigo-600" },
      "enabled": true,
      "created_at": "2024-09-13 14:12:56",
      "updated_at": "2024-09-13 14:12:56"
    }
  ]
}

Use sua aplicação

Vale para as redes da Meta (facebook_ads, facebook, instagram_business e instagram) e para os toolkits que conectam por OAuth. Nas redes da Meta, seus clientes conectam por padrão pelos apps do Reportei; cadastrando o seu próprio app, a tela de consentimento passa a mostrar a sua marca e o uso da API conta na cota do seu app. As demais integrações seguem nos apps do Reportei.

Toolkits que conectam por OAuth

Estas rotas também cadastram o app de um toolkit com modo oauth2: use o slug do toolkit no lugar de integration_slug. O toolkit não tem app do Reportei, então o modo oauth2 dele só conecta com o seu. Os passos, as permissões e os limites abaixo são das redes da Meta; o fluxo do toolkit está na seção Toolkits.

Chaves de LLM

As mesmas rotas cadastram a sua chave de API da OpenAI ou da Anthropic para o gateway de LLM, com openai ou anthropic no lugar do slug e o corpo {"api_key": "..."}. Detalhes na seção LLM.

Como configurar

1

Registre a callback_url de cada integração no seu app: em "Valid OAuth Redirect URIs" no Facebook Login, ou em "Business login settings" no Login do Instagram. O endereço segue o padrão abaixo e também volta na resposta do cadastro.

2

Cadastre o App ID e o App Secret com POST /merchants/credentials/{integration_slug}, informando em scopes só as permissões que o seu app tem.

3

Abra a sessão de integração como sempre. A partir daí, toda conexão nova dessa integração é feita com o seu app.

A callback_url de cada integração

Troque {integration_slug} pelo slug da integração (facebook_ads, facebook, instagram_business ou instagram) ou do toolkit. Se preferir receber o callback no seu domínio, informe redirect_url no cadastro e redirecione de lá para este endereço, com a query intacta.

https://connect.reportei.com/api/oauth/{integration_slug}/callback

Importante

Registre a callback_url no app antes de abrir a primeira sessão: a Meta recusa o login quando o endereço não está na lista do app.

Deixe "Require App Secret" desligado nas configurações avançadas do app.

As quatro integrações e o app de cada uma

Três integrações usam o mesmo app do Facebook. A API do Instagram com Login do Instagram tem um app próprio dentro do mesmo painel, com outro App ID e outro App Secret, então instagram é uma credencial separada.

integration_slugApp na MetaOnde registrar a callback_url
facebook_adsApp do Facebook, com Facebook Login for BusinessFacebook Login for Business → Settings → "Valid OAuth Redirect URIs"
facebookApp do Facebook, com Facebook Login for BusinessFacebook Login for Business → Settings → "Valid OAuth Redirect URIs"
instagram_businessApp do Facebook, com Facebook Login for BusinessFacebook Login for Business → Settings → "Valid OAuth Redirect URIs"
instagramInstagram app, com a API do Instagram com Login do InstagramInstagram → API setup with Instagram login → "Business login settings"

Permissões

Sem scopes, o Connect pede a lista padrão dos apps do Reportei, que inclui permissões que o seu app pode não ter. Cadastre em scopes só o que o seu app tem. Estas são as listas mínimas para coletar métricas:

integration_slugscopes mínimos
facebook_adsemail, pages_read_engagement, pages_read_user_content, read_insights, ads_read, ads_management, business_management, pages_show_list
facebookemail, public_profile, pages_read_engagement, pages_read_user_content, read_insights, business_management
instagram_businessads_management, ads_read, business_management, email, instagram_basic, instagram_manage_insights, pages_read_engagement, pages_read_user_content, pages_show_list, read_insights
instagraminstagram_business_basic, instagram_business_manage_insights

A sua lista substitui a padrão por inteiro. Se os seus clientes publicam pelo Connect, acrescente a permissão de publicação: pages_manage_posts para facebook, instagram_content_publish para instagram_business e instagram_business_content_publish para instagram.

GET/merchants/credentials/{integration_slug | toolkit_slug}

Consultar app

Retorna o app cadastrado para a integração ou para o toolkit, sem o client_secret, com a callback_url que precisa estar no app e quantas conexões ativas ele emitiu.

{
  "credential": {
    "integration": "facebook_ads",
    "client_id": "1234567890123456",
    "redirect_url": null,
    "scopes": null,
    "callback_url": "https://connect.reportei.com/api/oauth/facebook_ads/callback",
    "active_connections": 3,
    "created_at": "2026-10-13T12:00:00.000000Z",
    "updated_at": "2026-10-13T12:00:00.000000Z"
  }
}
POST/merchants/credentials/{integration_slug | toolkit_slug}

Cadastrar app

Cadastra o seu app para a integração da Meta ou para o toolkit OAuth, com o slug dele no caminho. A partir daí, toda conexão nova dos seus clientes pela sessão dessa integração é feita com ele; num toolkit que só conecta por OAuth, este cadastro vem antes da primeira sessão. Um app por integração ou toolkit: para trocar, use PUT.

Parâmetros

CampoDescrição
client_idApp ID do seu app na Meta, ou o client ID do app OAuth do toolkit (obrigatório). Para instagram, o do Instagram app.
client_secretApp Secret do mesmo app (obrigatório). Guardado criptografado e nunca devolvido pela API.
redirect_urlOpcional. Só se você quer receber o callback no seu domínio e redirecionar de lá para a callback_url. Precisa ser https. Em branco, o consentimento redireciona direto para a callback_url.
scopesOpcional. Lista de permissões que substitui a padrão por inteiro no consentimento. Em branco, vale a lista padrão: a dos apps do Reportei nas redes da Meta, ou a do toolkit, em auth[].scopes.
{
  "client_id": "1234567890123456",
  "client_secret": "YOUR_APP_SECRET",
  "scopes": [
    "email",
    "pages_read_engagement",
    "pages_read_user_content",
    "read_insights",
    "ads_read",
    "ads_management",
    "business_management",
    "pages_show_list"
  ]
}
PUT/merchants/credentials/{integration_slug | toolkit_slug}

Substituir app

Substituição completa: redirect_url e scopes ausentes voltam ao padrão. Trocar o client_id invalida as conexões ativas do app anterior, e o total vem em invalidated_connections. Um client_secret novo do mesmo app não invalida nada.

{
  "client_id": "6543210987654321",
  "client_secret": "YOUR_NEW_APP_SECRET",
  "redirect_url": "https://sua-aplicacao.com/oauth/meta/callback",
  "scopes": [
    "email",
    "pages_read_engagement",
    "pages_read_user_content",
    "read_insights",
    "ads_read",
    "ads_management",
    "business_management",
    "pages_show_list"
  ]
}
DELETE/merchants/credentials/{integration_slug | toolkit_slug}

Remover app

Nas redes da Meta, as conexões novas voltam aos apps do Reportei; num toolkit, a sessão volta ao formulário de chave, ou é recusada quando ele só conecta por OAuth. As conexões ativas feitas com o seu app são marcadas como expired, e o total vem em invalidated_connections.

{
  "message": "Credential removed",
  "invalidated_connections": 3
}

Trocar ou remover o app invalida as conexões

PUT com outro client_id, e DELETE, marcam como expired toda conexão ativa feita com o app anterior, porque os tokens dele não servem em outro app. A resposta traz o total em invalidated_connections, e cada cliente precisa reconectar pela sessão. Um client_secret novo do mesmo app (o reset do secret no painel da Meta) não invalida nada, e mudar só redirect_url ou scopes também não.

Essa invalidação não dispara o webhook customer_integration.status_updated. Use o total da resposta para avisar os seus clientes.

O que muda com o seu app

As conexões feitas antes do cadastro continuam nos apps do Reportei até o cliente reconectar. O campo active_connections diz quantas conexões ativas estão no seu app.

Nas redes da Meta, o Connect não verifica nem renova os tokens emitidos pelo seu app, e o token de usuário dura cerca de 60 dias. Quando a Meta expira ou invalida um deles, a conexão segue como active e a coleta volta sem dados até o cliente reconectar pela sessão.

No Instagram, stories e o histórico de seguidores (novos seguidores e a evolução no período) dependem de uma coleta diária feita com o app do Reportei, que não inclui as conexões feitas com o seu app. O total atual de seguidores vem normalmente.

Erros desta superfície

Além dos erros comuns da API, estes são próprios do cadastro do app. Falha de validação do corpo volta 422 com errors; as demais recusas voltam message e error_code.

Statuserror_codeDescrição
404credential_not_foundNenhum app cadastrado para esta integração.
409credential_already_existsJá existe um app cadastrado para esta integração. Troque com PUT ou remova com DELETE.
422credential_unsupported_integrationA integração ou o toolkit não aceita app próprio. Aceitam facebook_ads, facebook, instagram_business, instagram e os toolkits com modo oauth2.
422credential_not_availableEsta conta não pode cadastrar um app próprio.
422Falha de regra do corpo: campo obrigatório ausente, redirect_url sem https ou um scope com caractere inválido.

Customers

Gerencie os clientes associados ao seu merchant. Clientes representam os usuários finais que utilizarão as integrações.

GET/customers

Listar Clientes

Retorna uma lista paginada de todos os clientes associados ao seu merchant.

{
  "data": [
    {
      "uuid": "caedbbbe-7d93-476f-b965-bf97cd7ce874",
      "name": "Reportei Customer 1",
      "created_at": "2024-09-24 17:07:34",
      "updated_at": "2024-09-24 17:07:34",
      "trial_ends_at": "2024-10-08 17:15:12",
      "is_paying": true,
      "merchant": "Reportei"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 2,
    "total_pages": 1
  }
}
GET/customers/{uuid}

Obter Cliente

Retorna os detalhes de um cliente específico através do seu UUID.

{
  "customer": {
    "uuid": "caedbbbe-7d93-476f-b965-bf97cd7ce874",
    "name": "Reportei Customer 1",
    "created_at": "2024-09-24 17:07:34",
    "updated_at": "2024-09-24 17:07:34",
    "trial_ends_at": "2024-10-08 17:15:12",
    "is_paying": true,
    "merchant": "Reportei"
  }
}
POST/customers

Criar Cliente

Cria um novo cliente associado ao seu merchant. O api_token gerado neste momento é crucial e não poderá ser recuperado depois.

{
  "name": "Nome do Novo Cliente"
}
PUT/customers/{uuid}

Atualizar Cliente

Atualiza o nome de um cliente existente.

{
  "name": "Novo Nome do Cliente"
}
DELETE/customers/{uuid}

Deletar Cliente

Remove um cliente e todas as suas integrações associadas.

{
  "success": true,
  "message": "Record successfully removed"
}
POST/customers/{uuid}/update-payment-statusLegado

Atualizar Status de Pagamento

Marca um cliente como pagante ou não pagante. Funciona apenas para os merchants com cobrança por cliente (legado); nos demais, todo cliente já nasce pagante.

{
  "is_paying": true
}
GET/customers/settings

Configurações do Cliente

Retorna as configurações e integrações ativas de um cliente específico, utilizando o x-customer-token no header.

{
  "customer": {
    "uuid": "073c1e15-94d0-49b3-9861-a0e9b7ffb06c",
    "name": "Reportei Customer 1",
    "created_at": "2024-09-24 19:06:08",
    "updated_at": "2024-09-24 19:06:08",
    "trial_ends_at": "2024-10-08 17:15:12",
    "is_paying": true,
    "merchant": "Reportei",
    "integrations": [
      {
        "uuid": "6110b666-0327-4a81-b48e-9feb34d72f78",
        "integration": "Instagram Business",
        "name": "reportei",
        "slug": "instagram_business",
        "status": "active"
      }
    ]
  }
}

Customer Integrations

Gerencie as conexões das plataformas de redes sociais e marketing dos seus clientes. Utilize o x-customer-token para acessar estes endpoints.

Status da Integração

Cada integração de cliente possui um status que indica sua validade e conexão. Verifique o campo status para entender o estado da conexão.

activeexpiredrevoked
POST/customer-integrations/session

Criar Sessão de Integração

Inicia um fluxo para que o cliente possa conectar novas contas de redes sociais ou gerenciar as existentes. Retorna um link único para a interface de integração.

Todos os campos são opcionais.

CampoDescrição
redirect_urlURL de redirecionamento após a integração
localeIdioma da interface de integração (ex: pt_BR, en, es, fr)
limitsArray de objetos para controlar o comportamento da sessão (veja os limites disponíveis abaixo)
limit_reached_urlURL para redirecionar quando o limite de integrações for atingido
expires_in_minutesTempo de expiração da sessão em minutos (mínimo: 1)
close_on_finishSe true, fecha a janela ao finalizar a integração
enable_multi_account_selectionPermite que o usuário selecione múltiplas contas na mesma sessão
toolkit_slugAbre a sessão num toolkit, pelo slug de GET /toolkits, em vez do catálogo de plataformas. Não aceita source_slug junto. Ao terminar, o cliente volta para a redirect_url com ?customer_toolkit={uuid}. Veja a seção Toolkits.

Limits disponíveis

O campo limits recebe um array de objetos, cada um com name e value. É possível combinar múltiplos limites na mesma sessão.

integration_countintegerbloqueia

Limita o número total de integrações que o customer pode conectar nesta sessão.

{ "name": "integration_count", "value": 5 }
same_type_integrationintegerbloqueia

Limita quantas integrações do mesmo tipo (ex: Instagram Business) o customer pode conectar.

{ "name": "same_type_integration", "value": 2 }
same_integration_across_customersintegerbloqueia

Legado, só para os merchants com cobrança por cliente: limita quantos customers não pagantes do mesmo merchant podem conectar a mesma conta.

{ "name": "same_integration_across_customers", "value": 1 }
available_integrationsarray de slugsfiltro de UI

Filtra quais integrações são exibidas na tela de conexão. Não bloqueia, apenas controla a visibilidade. Se omitido, todas as integrações do merchant são exibidas.

{ "name": "available_integrations", "value": ["instagram_business", "facebook_ads", "google_analytics_4"] }

Exemplo combinando múltiplos limites

{
  "redirect_url": "https://sua-aplicacao.com/dashboard",
  "limits": [
    { "name": "integration_count", "value": 3 },
    { "name": "available_integrations", "value": ["instagram_business", "facebook_ads"] }
  ],
  "limit_reached_url": "https://sua-aplicacao.com/upgrade"
}

Sessão expirável

O link da sessão é válido por 5 minutos. Acesse o session_link para iniciar o processo de integração do cliente.

{
  "redirect_url": "https://sua-aplicacao.com/dashboard",
  "locale": "pt_BR",
  "limits": {},
  "limit_reached_url": "https://sua-aplicacao.com/limite",
  "expires_in_minutes": 30,
  "close_on_finish": true,
  "enable_multi_account_selection": false
}
GET/customer-integrations/{uuid}

Obter Integração

Retorna detalhes de uma integração específica do cliente.

{
  "customer_integration": {
    "uuid": "4addc0da-8583-4dbb-a0e9-c7e2e8470f50",
    "source_name": "reportei",
    "status": "active",
    "created_at": "2024-09-24 17:08:08",
    "updated_at": "2024-09-24 17:08:08",
    "integration": {
      "name": "Instagram Business",
      "slug": "instagram_business"
    }
  }
}
DELETE/customer-integrations/{uuid}?session_id={uuid}

Deletar Integração

Remove uma integração específica do cliente. Requer o UUID da integração e o UUID de uma sessão ativa.

{
  "success": true,
  "message": "Record successfully removed"
}

Metrics

Consulte métricas de integrações conectadas. Descubra as métricas que cada plataforma oferece em GET /metrics/{integration_slug}, teste a coleta no Connect Explorer e use /metrics/get-data para obter os dados programaticamente.

Connect Explorer

O Connect Explorer é uma ferramenta interativa que permite validar seus tokens, explorar as métricas disponíveis por integração e testar consultas à API, tudo sem escrever código. Ideal para descobrir quais métricas e dimensões estão disponíveis antes de implementar a integração.

1

Valide seus tokens

Acesse o Explorer, insira seu Bearer Token (merchant) e x-customer-token (customer) para validar a conexão e carregar as integrações disponíveis.

Valide seus tokens
2

Explore métricas disponíveis

Selecione uma integração e navegue pelo catálogo completo de métricas, organizadas por tipo (Numbers, Charts, Tables). Customize métricas e dimensões conforme necessário.

Explore métricas disponíveis
3

Teste a consulta e copie o payload

Defina o período desejado, execute a consulta e visualize a resposta da API em tempo real. Copie o payload gerado para usar diretamente nos endpoints /metrics/get-data ou /metrics/get-data-async.

Teste a consulta e copie o payload
Acessar Connect Explorer

Estrutura de Métricas

Cada métrica no array metrics possui a seguinte estrutura:

CampoDescrição
idIdentificador único para a métrica. Use qualquer UUID ou string única.
reference_keyNome geral da métrica, ex: ig:story_replies (respostas de stories do Instagram). Os disponíveis por plataforma vêm de GET /metrics/{integration_slug}.
metricsArray com os valores específicos de métricas solicitados, ex: ["replies"].
dimensionsOpcionalDetalhamento adicional da métrica, ex: ["stories", "date", "device"].
componentTipo da métrica, que altera a estrutura da resposta final.
entity_idOpcionalJunto com entity_type, informa o ID e o tipo da entidade para filtrar resultados (ad, campaign ou adset).

Componentes disponíveis

componentDescrição
number_v1Retorna um único valor numérico.
datatable_v1Retorna dados tabulares.
chart_v1Retorna dados formatados para gráficos.

Importante

Dependendo da rede da integração do cliente, algumas combinações de métricas e dimensões podem não ser compatíveis.

GET/metrics/{integration_slug}

Listar Métricas Disponíveis

Retorna todas as métricas que uma plataforma oferece, com reference_key, component, metrics e dimensions padrão, além dos valores aceitos em meta.setup. Requer apenas o Bearer Token.

Parâmetros obrigatórios

NomeDescrição
integration_slugSlug da plataforma, como devolvido por GET /integrations (ex.: facebook_ads)

Parâmetros opcionais

NomeDescrição
languageIdioma dos títulos e labels: en (padrão), pt_br, es, fr, it, de

Cada item de data já é uma métrica válida para o array metrics de /metrics/get-data: copie id, reference_key, component, metrics e dimensions. Para pedir outro valor ou detalhamento, troque metrics e dimensions por chaves de meta.setup. O catálogo muda raramente e é cacheado pelo Connect: busque uma vez e guarde do seu lado.

{
  "data": [
    {
      "id": "c45d148c-0d07-4473-b7d7-f886c8b56884",
      "reference_key": "fb_ads:spend",
      "component": "number_v1",
      "metrics": ["spend"],
      "dimensions": [],
      "references": {
        "title": "Valor investido",
        "description": "O valor total estimado que você gastou na sua campanha, conjunto de anúncios ou anúncio durante seu agendamento.",
        "tags": { "cost": "pago", "history": "retroativo" }
      }
    },
    {
      "id": "80e06171-759e-472e-9cff-25ae8e91e1e0",
      "reference_key": "fb_ads:insights_by_campaign",
      "component": "datatable_v1",
      "metrics": ["results", "cost_per_results", "spend", "reach", "impressions", "ctr", "cpc", "cpm", "frequency"],
      "dimensions": [{ "field": "campaign" }],
      "references": {
        "title": "Campanhas em destaque",
        "description": "Campanhas que tiveram o maior impacto em sua audiência, no período analisado.",
        "tags": { "cost": "pago", "history": "retroativo" }
      }
    }
  ],
  "meta": {
    "integration_slug": "facebook_ads",
    "language": "pt_br",
    "setup": {
      "metrics": {
        "spend": { "label": "Valor investido" },
        "results": { "label": "Resultados" },
        "reach": { "label": "Alcance" },
        "impressions": { "label": "Impressões" }
      },
      "dimensions": {
        "campaign": { "label": "Nome da Campanha" },
        "publisher_platform": { "label": "Plataforma" },
        "date": { "label": "Data" }
      }
    }
  }
}
POST/metrics/get-data

Obter Dados de Métricas

Retorna um objeto com os valores das métricas solicitadas de uma integração de cliente.

Parâmetros obrigatórios

NomeDescrição
customer_integrationUUID da integração do cliente
startData de início da análise (ISO 8601)
endData de fim da análise (ISO 8601)
metricsArray contendo a estrutura de métricas

Parâmetros opcionais

NomeDescrição
comparison_startData de início da comparação (ISO 8601)
comparison_endData de fim da comparação (ISO 8601)
{
  "customer_integration": "4addc0da-8583-4dbb-a0e9-c7e2e8470f50",
  "start": "2024-01-01",
  "end": "2024-09-01",
  "metrics": [
    {
      "id": "f35a44ce-dac5-4188-aace-5c44a8952176",
      "reference_key": "ig:story_replies",
      "component": "number_v1",
      "metrics": ["replies"],
      "dimensions": ["stories"]
    },
    {
      "id": "3051ed66-a05c-462a-aece-e1d900e78b02",
      "reference_key": "ig:followers_gender",
      "component": "chart_v1",
      "metrics": ["followers"],
      "dimensions": ["gender"]
    },
    {
      "id": "b3a949ac-f6ae-4bd1-bb90-1e8e0cbe8ed5",
      "reference_key": "ig:clicks_breakdown",
      "component": "datatable_v1",
      "metrics": ["count", "ctr"],
      "dimensions": ["clicks_breakdown"]
    }
  ]
}
POST/metrics/get-data-async

Obter Dados de Métricas (Assíncrono)

Inicia um processo para coletar os dados de métricas solicitados e envia os resultados de forma assíncrona para a metrics_webhook_url fornecida.

Parâmetros obrigatórios

NomeDescrição
metrics_webhook_urlURL que receberá o payload de métricas
customer_integrationUUID da integração do cliente
startData de início da análise (ISO 8601)
endData de fim da análise (ISO 8601)
metricsArray contendo a estrutura de métricas

Parâmetros opcionais

NomeDescrição
comparison_startData de início da comparação (ISO 8601)
comparison_endData de fim da comparação (ISO 8601)

A estrutura do corpo da requisição é idêntica ao endpoint /metrics/get-data, e o payload enviado para a metrics_webhook_url segue o mesmo formato da resposta daquele endpoint. A chamada retorna 200 OK assim que o processo de coleta é iniciado.

{
  "metrics_webhook_url": "https://sua-aplicacao.com/webhook/metrics",
  "customer_integration": "4addc0da-8583-4dbb-a0e9-c7e2e8470f50",
  "start": "2024-01-01",
  "end": "2024-09-01",
  "metrics": [
    {
      "id": "f35a44ce-dac5-4188-aace-5c44a8952176",
      "reference_key": "ig:story_replies",
      "component": "number_v1",
      "metrics": ["replies"],
      "dimensions": ["stories"]
    },
    {
      "id": "3051ed66-a05c-462a-aece-e1d900e78b02",
      "reference_key": "ig:followers_gender",
      "component": "chart_v1",
      "metrics": ["followers"],
      "dimensions": ["gender"]
    },
    {
      "id": "b3a949ac-f6ae-4bd1-bb90-1e8e0cbe8ed5",
      "reference_key": "ig:clicks_breakdown",
      "component": "datatable_v1",
      "metrics": ["count", "ctr"],
      "dimensions": ["clicks_breakdown"]
    }
  ]
}

Advertising

Crie e gerencie anúncios na conta de anúncios do seu cliente sem implementar a API da integração. Hoje apenas Meta (`facebook_ads`). As rotas de um cliente exigem o Bearer do merchant, o `x-customer-token` do cliente e a conexão em `?customer_integration=`, sempre na query. A conta de anúncios sai da conexão; `ad_account_id` no corpo é ignorado. O panorama de todos os clientes, em `/merchants/ads/*`, pede só o Bearer e fica na seção Merchants.

IDs nos endpoints

Onde um endpoint de ads pede um id, vale tanto o `uuid` do Connect quanto o `id` do objeto, o numérico que a plataforma de anúncios deu à campanha, ao conjunto ou ao anúncio (na Meta, o mesmo do Gerenciador de Anúncios). Isso inclui o `{id}` da rota, o `id` de um nível no corpo (`campaign: { "id": … }`) e filtros como `campaign_id`. O `id` funciona para qualquer objeto da conta. O `uuid` só funciona para os que o Connect guarda. As respostas trazem os dois: `id` é o do objeto e `uuid` é o do Connect, que vem `null` quando o objeto não está no Connect.

Campos de cada integração

Em cada nível, os campos que toda integração de anúncios tem ficam no topo (`name`, `status`, `budget`, `schedule`, `targeting`…). O que é só de uma integração vai num bloco com o nome do `integration_slug` dela. Hoje esse bloco é `facebook_ads: { … }`, com objetivo, otimização, lance, posicionamentos e interesses. O conteúdo do bloco é da integração e pode ganhar campos novos. Outra integração terá o próprio bloco, com campos diferentes (por exemplo, `google_ads: { … }`), e os campos do topo continuam os mesmos. Os campos e valores aceitos em cada bloco estão em `GET /ads/options?integration_slug=`.

Esta é a única superfície que gasta dinheiro de verdade

Tudo que uma escrita cria nasce `paused`. Quando você pede `status: active` no `POST /ads`, a ativação é o último passo, depois que a árvore inteira existe. Uma falha no meio nunca deixa nada gastando.

Se a criação quebrar no meio, o que já foi criado é desfeito em ordem inversa. O que não for possível desfazer volta em `created`, com nível e id, para você remover.

Toda escrita é idempotente pelo header `Idempotency-Key` (obrigatório nas criações). Repetir com a mesma chave e o mesmo pedido devolve a primeira resposta com `Idempotent-Replay: true`; depois de uma falha, a repetição retoma de onde parou em vez de criar uma segunda campanha. Gere a chave uma vez por operação, antes da primeira tentativa, e reenvie a mesma em toda repetição: uma chave nova a cada tentativa cria outra campanha.

O Connect guarda o que ele cria

As listagens de campanhas, conjuntos e anúncios leem do banco do Connect por padrão (`source=connect`): o que o Connect criou ou já mexeu nesta conta, com `uuid` e `synced_at`. Uma conta que já tinha campanhas lista vazio até a primeira escrita pelo Connect. Use `source=external` para ler a conta ao vivo. Qualquer escrita aceita o id de qualquer objeto da conta, e o objeto entra no banco nessa hora (`origin: imported`). A paginação é a padrão: `page` e `per_page`. Cada objeto traz `customer_uuid` e `customer_integration_uuid`: o cliente dono da conta e a conexão pela qual o Connect a alcançou por último. Os dois são `null` quando o objeto não está no banco do Connect, e a conexão também fica `null` depois de apagada.

Status efetivo e problemas

`status` é o que você pediu (`active` ou `paused`). `effective_status` é o que a plataforma aplica de fato, somando o objeto, os níveis acima dele e a revisão: `pending_review` em análise, `disapproved` reprovado, `with_issues` com problemas, `campaign_paused` ou `adset_paused` quando um nível acima está pausado. Nas listagens com `source=connect`, o filtro `effective_status` traz, por exemplo, só os anúncios reprovados; com `source=external`, ele responde 422.

`issues` diz por que a plataforma segura o objeto: uma lista de `{ code, message }` com os problemas de entrega em qualquer nível e, num anúncio, o motivo da reprovação (`code: ad_review`). Lista vazia quando não há nada a relatar; `null` quando o estado ainda não foi lido. `refresh=true` relê `status`, `effective_status`, `issues`, `name` e `synced_at` na integração.

Opções

Você fala sempre o mesmo vocabulário em snake_case e o Connect traduz para a integração: `traffic` vira `OUTCOME_TRAFFIC`, e um orçamento de 50 vira a unidade mínima da moeda da conta. Em cada nível, o que toda integração de anúncios tem fica no topo, e o que é só da Meta vai em `facebook_ads`. Leia daqui o que é aceito, em vez de adivinhar.

GET/ads/options

Vocabulário aceito

Publica, nível a nível, o que a integração aceita: os valores dos campos comuns, onde o orçamento pode ficar e os campos do bloco `facebook_ads`. Não depende de conexão: passe `integration_slug`.

{
  "data": {
    "integration_slug": "facebook_ads",
    "budget": {
      "levels": [
        "campaign",
        "adset"
      ],
      "types": [
        "daily",
        "lifetime"
      ]
    },
    "common": {
      "status": [
        "active",
        "paused"
      ],
      "format": [
        "image",
        "video"
      ],
      "call_to_action": [
        "shop_now",
        "learn_more",
        "sign_up"
      ],
      "targeting": [
        "countries",
        "age_min",
        "age_max",
        "genders"
      ],
      "genders": [
        "all",
        "male",
        "female"
      ]
    },
    "facebook_ads": {
      "campaign": {
        "objective": [
          "traffic",
          "engagement",
          "leads",
          "sales",
          "awareness",
          "app_promotion"
        ],
        "fields": [
          "objective",
          "special_ad_categories",
          "spend_cap"
        ]
      },
      "adset": {
        "optimization_goal": [
          "link_clicks",
          "landing_page_views",
          "post_engagement"
        ],
        "billing_event": [
          "impressions",
          "link_clicks"
        ],
        "bid_strategy": [
          "lowest_cost",
          "bid_cap",
          "cost_cap"
        ],
        "fields": [
          "optimization_goal",
          "billing_event",
          "bid_strategy",
          "bid_amount",
          "placements",
          "targeting",
          "promoted_object"
        ]
      },
      "ad": {
        "required": [
          "page_id"
        ],
        "fields": [
          "page_id",
          "instagram_account_id",
          "url_tags",
          "tracking_specs"
        ]
      }
    }
  }
}

Campanhas

O nível mais alto da árvore: define o objetivo (`facebook_ads.objective`) e, no modo CBO, o orçamento. Listar, criar, detalhar, atualizar, duplicar e remover. Remover uma campanha leva junto tudo que está abaixo dela.

GET/ads/campaigns

Listar campanhas

Do banco do Connect por padrão; com `source=external`, a conta ao vivo. Filtros `status`, `effective_status` e `include_deleted` (os dois últimos só com `source=connect`); paginação por `page` e `per_page`.

{
  "data": [
    {
      "id": "120250000000000001",
      "uuid": "9a1d3f52-7c1e-4c55-9b0e-5c3f1c6a2e10",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "name": "Black Friday",
      "status": "paused",
      "effective_status": "paused",
      "issues": [],
      "budget": {
        "amount": 50,
        "type": "daily",
        "level": "campaign",
        "currency": "BRL"
      },
      "adsets_count": 1,
      "facebook_ads": {
        "objective": "traffic",
        "spend_cap": null,
        "bid_strategy": "lowest_cost"
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/ads/campaigns?page=1",
    "last": "https://connect.reportei.com/api/ads/campaigns?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/ads/campaigns",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
POST/ads/campaigns

Criar campanha

O objetivo vai em `facebook_ads.objective`; com `budget`, a campanha é CBO. Sem `status` nasce pausada. Exige `Idempotency-Key`.

{
  "name": "Black Friday",
  "budget": {
    "amount": 50,
    "type": "daily"
  },
  "facebook_ads": {
    "objective": "traffic",
    "special_ad_categories": []
  }
}
GET/ads/campaigns/{id}

Detalhar campanha

Pelo `id` da campanha ou pelo `uuid` do Connect. `refresh=true` relê `status`, `effective_status`, `issues` e `name` na integração; `source=external` lê ao vivo.

{
  "data": {
    "id": "120250000000000001",
    "uuid": "9a1d3f52-7c1e-4c55-9b0e-5c3f1c6a2e10",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
    "origin": "created",
    "synced_at": "2026-11-19T10:00:00-03:00",
    "deleted_at": null,
    "name": "Black Friday",
    "status": "paused",
    "effective_status": "paused",
    "issues": [],
    "budget": {
      "amount": 50,
      "type": "daily",
      "level": "campaign",
      "currency": "BRL"
    },
    "adsets_count": 1,
    "facebook_ads": {
      "objective": "traffic",
      "spend_cap": null,
      "bid_strategy": "lowest_cost"
    },
    "created_at": "2026-11-19T10:00:00-0300",
    "updated_at": "2026-11-19T10:00:00-0300"
  }
}
PATCH/ads/campaigns/{id}

Atualizar campanha

Vale para qualquer campanha da conta; uma criada fora entra no banco nesta escrita. Orçamento numa campanha ABO volta 409 `budget_level_mismatch`.

{
  "status": "active",
  "budget": {
    "amount": 80
  }
}
POST/ads/campaigns/{id}/duplicate

Duplicar campanha

Copia a campanha sem os conjuntos e anúncios. A cópia mantém objetivo, nível do orçamento (CBO/ABO) e categorias especiais; você manda o `name` e, se quiser, `status` e `budget`. Um `budget` numa campanha ABO volta 409 `budget_level_mismatch`, antes de copiar. Exige `Idempotency-Key`.

A cópia nasce pausada e, com `status: active`, é ativada no último passo. Se a integração recusar o orçamento ou a agenda, a cópia é apagada, e repetir com a mesma `Idempotency-Key` copia de novo. Se só a ativação falhar, a cópia fica, pausada e completa, e volta em `created`; repetir com a mesma chave só ativa.

{
  "name": "Black Friday | 02",
  "budget": {
    "amount": 150,
    "type": "daily"
  }
}
DELETE/ads/campaigns/{id}

Remover campanha

Remove a campanha e, em cascata, os conjuntos e anúncios abaixo dela, na integração e no Connect.

{
  "data": {
    "id": "120250000000000001",
    "deleted": true
  }
}

Conjuntos de anúncios

Onde moram público, posicionamento, agenda e, no modo ABO, o orçamento. O `PATCH` de público é substituição total na integração, então o Connect lê o conjunto atual e mescla o que você mandou: mandar parte de um público não apaga o resto. Duplicar copia o conjunto sem os anúncios, para a mesma campanha ou outra.

GET/ads/adsets

Listar conjuntos

Mesmas regras das campanhas. `campaign_id` aceita o `id` da campanha ou o `uuid`.

{
  "data": [
    {
      "id": "120250000000000002",
      "uuid": "1b2c3d4e-5f60-4718-8a9b-0c1d2e3f4a5b",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "campaign_id": "120250000000000001",
      "name": "BR 25-45",
      "status": "paused",
      "effective_status": "paused",
      "issues": [],
      "budget": null,
      "schedule": {
        "start": "2026-11-20T00:00:00-0300",
        "end": null
      },
      "targeting": {
        "countries": [
          "BR"
        ],
        "age_min": 25,
        "age_max": 45,
        "genders": [
          "all"
        ]
      },
      "facebook_ads": {
        "optimization_goal": "link_clicks",
        "billing_event": "impressions",
        "bid_strategy": null,
        "bid_amount": null,
        "placements": {
          "publisher_platforms": [
            "facebook",
            "instagram"
          ]
        },
        "targeting": {
          "interests": [
            {
              "id": "6003139266461",
              "name": "Moda"
            }
          ],
          "custom_audiences": [],
          "excluded_custom_audiences": [],
          "advantage_audience": false,
          "raw": {}
        }
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/ads/adsets?page=1",
    "last": "https://connect.reportei.com/api/ads/adsets?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/ads/adsets",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
POST/ads/adsets

Criar conjunto

Público comum em `targeting`; meta de otimização, lance, posicionamentos e interesses em `facebook_ads`. Orçamento sob uma campanha CBO volta 409.

{
  "campaign_id": "120250000000000001",
  "budget": {
    "amount": 20,
    "type": "daily"
  },
  "name": "BR 25-45",
  "schedule": {
    "start": "2026-11-20T00:00:00-03:00"
  },
  "targeting": {
    "countries": [
      "BR"
    ],
    "age_min": 25,
    "age_max": 45
  },
  "facebook_ads": {
    "optimization_goal": "link_clicks",
    "billing_event": "impressions",
    "placements": {
      "publisher_platforms": [
        "facebook",
        "instagram"
      ]
    },
    "targeting": {
      "interests": [
        {
          "id": "6003139266461",
          "name": "Moda"
        }
      ],
      "advantage_audience": false
    }
  }
}
GET/ads/adsets/{id}

Detalhar conjunto

Um conjunto pelo id ou `uuid`, com público e agenda no vocabulário do Connect.

{
  "data": {
    "id": "120250000000000002",
    "uuid": "1b2c3d4e-5f60-4718-8a9b-0c1d2e3f4a5b",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
    "origin": "created",
    "synced_at": "2026-11-19T10:00:00-03:00",
    "deleted_at": null,
    "campaign_id": "120250000000000001",
    "name": "BR 25-45",
    "status": "paused",
    "effective_status": "paused",
    "issues": [],
    "budget": null,
    "schedule": {
      "start": "2026-11-20T00:00:00-0300",
      "end": null
    },
    "targeting": {
      "countries": [
        "BR"
      ],
      "age_min": 25,
      "age_max": 45,
      "genders": [
        "all"
      ]
    },
    "facebook_ads": {
      "optimization_goal": "link_clicks",
      "billing_event": "impressions",
      "bid_strategy": null,
      "bid_amount": null,
      "placements": {
        "publisher_platforms": [
          "facebook",
          "instagram"
        ]
      },
      "targeting": {
        "interests": [
          {
            "id": "6003139266461",
            "name": "Moda"
          }
        ],
        "custom_audiences": [],
        "excluded_custom_audiences": [],
        "advantage_audience": false,
        "raw": {}
      }
    },
    "created_at": "2026-11-19T10:00:00-0300",
    "updated_at": "2026-11-19T10:00:00-0300"
  }
}
PATCH/ads/adsets/{id}

Atualizar conjunto

Alteração parcial. O público é mesclado com o atual, então mandar um campo não apaga os outros.

{
  "targeting": {
    "age_min": 30
  },
  "facebook_ads": {
    "placements": {
      "publisher_platforms": [
        "instagram"
      ]
    }
  }
}
POST/ads/adsets/{id}/duplicate

Duplicar conjunto

Copia o conjunto sem os anúncios, para a campanha de `campaign_id` (`id` ou `uuid`) ou, sem ele, para a campanha do modelo. Público, posicionamentos, otimização e objeto promovido vêm do modelo; `name`, `status`, `budget` e `schedule` podem mudar. Um `budget` com a campanha de destino em CBO volta 409 `budget_level_mismatch`, antes de copiar. Exige `Idempotency-Key`.

A cópia nasce pausada e, com `status: active`, é ativada no último passo. Se a integração recusar o orçamento ou a agenda, a cópia é apagada, e repetir com a mesma `Idempotency-Key` copia de novo. Se só a ativação falhar, a cópia fica, pausada e completa, e volta em `created`; repetir com a mesma chave só ativa.

{
  "name": "Público quente | 02",
  "campaign_id": "120250000000000001",
  "schedule": {
    "start": "2026-11-20T00:00:00-03:00"
  }
}
DELETE/ads/adsets/{id}

Remover conjunto

Remove o conjunto e os anúncios dentro dele.

{
  "data": {
    "id": "120250000000000002",
    "deleted": true
  }
}

Anúncios

O criativo e o que ele mostra. O `POST /ads` cria o anúncio e o que faltar acima dele, em uma chamada; `POST /ads/boost` faz o mesmo promovendo um post existente; `POST /ads/validate` ensaia sem criar.

FormaO que a disparaO que cria
Completacampaign + adset + ad
Sob campanha existentecampaign: { id }adset + ad
Anexar a conjunto existenteadset: { id }ad
Impulsionar post orgânicoPOST /ads/boosta mesma árvore, com o post no lugar do criativo

Nada é deduzido do corpo: cada nível usa um existente (`{ "id": … }`) ou descreve o que será criado. Um nível com `id` não aceita outro campo, chaves desconhecidas são recusadas com 422, e o orçamento fica na campanha (CBO) ou no conjunto (ABO), nunca nos dois. O que é só da Meta vai no bloco `facebook_ads` de cada nível.

GET/ads

Listar anúncios

Do banco do Connect por padrão. Filtros `campaign_id` e `adset_id` aceitam id ou `uuid`.

{
  "data": [
    {
      "id": "120250000000000003",
      "uuid": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
      "origin": "created",
      "synced_at": "2026-11-19T10:00:00-03:00",
      "deleted_at": null,
      "campaign_id": "120250000000000001",
      "adset_id": "120250000000000002",
      "name": "Black Friday, imagem",
      "status": "paused",
      "effective_status": "paused",
      "issues": [],
      "format": "image",
      "headline": "Black Friday",
      "body": "Até 50% off",
      "description": null,
      "link_url": "https://loja.com/bf",
      "call_to_action": "shop_now",
      "thumbnail_url": null,
      "facebook_ads": {
        "creative_id": "120250000000000004",
        "page_id": "1068595869664481",
        "instagram_account_id": null,
        "post_id": null
      },
      "created_at": "2026-11-19T10:00:00-0300",
      "updated_at": "2026-11-19T10:00:00-0300"
    }
  ],
  "links": {
    "first": "https://connect.reportei.com/api/ads?page=1",
    "last": "https://connect.reportei.com/api/ads?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/ads",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
POST/ads

Criar a árvore completa

Cria campanha, conjunto e anúncio em uma chamada. Tudo nasce pausado e, com `status: active`, é ativado no último passo. Exige `Idempotency-Key`.

{
  "name": "Black Friday, imagem",
  "status": "active",
  "campaign": {
    "name": "Black Friday",
    "budget": {
      "amount": 50,
      "type": "daily"
    },
    "facebook_ads": {
      "objective": "traffic",
      "special_ad_categories": []
    }
  },
  "adset": {
    "name": "BR 25-45",
    "schedule": {
      "start": "2026-11-20T00:00:00-03:00"
    },
    "targeting": {
      "countries": [
        "BR"
      ],
      "age_min": 25,
      "age_max": 45
    },
    "facebook_ads": {
      "optimization_goal": "link_clicks",
      "billing_event": "impressions",
      "placements": {
        "publisher_platforms": [
          "facebook",
          "instagram"
        ]
      },
      "targeting": {
        "interests": [
          {
            "id": "6003139266461",
            "name": "Moda"
          }
        ],
        "advantage_audience": false
      }
    }
  },
  "ad": {
    "format": "image",
    "headline": "Black Friday",
    "body": "Até 50% off",
    "link_url": "https://loja.com/bf",
    "call_to_action": "shop_now",
    "media": [
      {
        "type": "image",
        "url": "https://cdn.loja.com/bf.jpg"
      }
    ],
    "facebook_ads": {
      "page_id": "1068595869664481"
    }
  }
}
POST/ads

Criar sob campanha existente

`campaign: { "id": … }` usa uma campanha que já existe; o conjunto e o anúncio são criados. O exemplo é um anúncio de vídeo: `thumbnail_url` é obrigatório.

{
  "name": "Black Friday, vídeo",
  "campaign": {
    "id": "120250000000000001"
  },
  "adset": {
    "name": "BR 25-45",
    "schedule": {
      "start": "2026-11-20T00:00:00-03:00"
    },
    "targeting": {
      "countries": [
        "BR"
      ],
      "age_min": 25,
      "age_max": 45
    },
    "facebook_ads": {
      "optimization_goal": "link_clicks",
      "billing_event": "impressions",
      "placements": {
        "publisher_platforms": [
          "facebook",
          "instagram"
        ]
      },
      "targeting": {
        "interests": [
          {
            "id": "6003139266461",
            "name": "Moda"
          }
        ],
        "advantage_audience": false
      }
    }
  },
  "ad": {
    "format": "video",
    "link_url": "https://loja.com/bf",
    "media": [
      {
        "type": "video",
        "url": "https://cdn.loja.com/bf.mp4",
        "thumbnail_url": "https://cdn.loja.com/bf.jpg"
      }
    ],
    "facebook_ads": {
      "page_id": "1068595869664481"
    }
  }
}
POST/ads

Anexar a conjunto existente

`adset: { "id": … }` cria só o anúncio. A campanha não pode ser enviada junto.

{
  "name": "Black Friday, variação",
  "adset": {
    "id": "120250000000000002"
  },
  "ad": {
    "format": "image",
    "headline": "Black Friday",
    "body": "Até 50% off",
    "link_url": "https://loja.com/bf",
    "call_to_action": "shop_now",
    "media": [
      {
        "type": "image",
        "url": "https://cdn.loja.com/bf.jpg"
      }
    ],
    "facebook_ads": {
      "page_id": "1068595869664481"
    }
  }
}
POST/ads

Criar anúncio com versão de feed e de story

O mesmo criativo com dois arquivos: um em `feed` (Feed do Facebook e do Instagram) e outro em `story` (Stories e Reels), cada um com `placements`. Textos, link e botão valem para os dois. Com dois arquivos, cada um é nomeado pelo que a conta já tem, o `hash` da imagem ou o `id` do vídeo de `POST /ads/facebook_ads/media`, nunca por `url`. Os dois têm o tipo do `format`, e vídeo leva `thumbnail_url`.

{
  "name": "Black Friday, feed e story",
  "adset": {
    "id": "120250000000000002"
  },
  "ad": {
    "format": "video",
    "headline": "Black Friday",
    "body": "Até 50% off",
    "link_url": "https://loja.com/bf",
    "call_to_action": "shop_now",
    "media": [
      {
        "type": "video",
        "id": "120210000000001",
        "thumbnail_url": "https://cdn.loja.com/bf-feed.jpg",
        "placements": ["feed"]
      },
      {
        "type": "video",
        "id": "120210000000002",
        "thumbnail_url": "https://cdn.loja.com/bf-story.jpg",
        "placements": ["story"]
      }
    ],
    "facebook_ads": {
      "page_id": "1068595869664481"
    }
  }
}
POST/ads/boost

Impulsionar um post orgânico

A mesma árvore, com `ad.post_id` no lugar do criativo. Exige `facebook_ads.instagram_account_id`, que é como a posse do post é verificada. Textos e mídia são recusados.

{
  "name": "Impulsionar post",
  "status": "active",
  "campaign": {
    "facebook_ads": {
      "objective": "engagement"
    }
  },
  "adset": {
    "budget": {
      "amount": 20,
      "type": "daily"
    },
    "targeting": {
      "countries": [
        "BR"
      ]
    },
    "facebook_ads": {
      "optimization_goal": "post_engagement",
      "billing_event": "impressions"
    }
  },
  "ad": {
    "post_id": "18191649508387596",
    "facebook_ads": {
      "instagram_account_id": "17841442920331135"
    }
  }
}
POST/ads/validate

Validar sem criar

O mesmo corpo do `POST /ads`. A integração valida cada nível, e nada é criado.

A integração não valida a árvore inteira: ela recusa validar um conjunto contra uma campanha que ainda não existe. Por isso cada nível que a chamada teria de criar antes volta como `skipped`, e não como erro. O dry run não precisa de `Idempotency-Key`.

{
  "name": "Black Friday, imagem",
  "status": "active",
  "campaign": {
    "name": "Black Friday",
    "budget": {
      "amount": 50,
      "type": "daily"
    },
    "facebook_ads": {
      "objective": "traffic",
      "special_ad_categories": []
    }
  },
  "adset": {
    "name": "BR 25-45",
    "schedule": {
      "start": "2026-11-20T00:00:00-03:00"
    },
    "targeting": {
      "countries": [
        "BR"
      ],
      "age_min": 25,
      "age_max": 45
    },
    "facebook_ads": {
      "optimization_goal": "link_clicks",
      "billing_event": "impressions",
      "placements": {
        "publisher_platforms": [
          "facebook",
          "instagram"
        ]
      },
      "targeting": {
        "interests": [
          {
            "id": "6003139266461",
            "name": "Moda"
          }
        ],
        "advantage_audience": false
      }
    }
  },
  "ad": {
    "format": "image",
    "headline": "Black Friday",
    "body": "Até 50% off",
    "link_url": "https://loja.com/bf",
    "call_to_action": "shop_now",
    "media": [
      {
        "type": "image",
        "url": "https://cdn.loja.com/bf.jpg"
      }
    ],
    "facebook_ads": {
      "page_id": "1068595869664481"
    }
  }
}
GET/ads/{id}

Detalhar anúncio

Um anúncio pelo id ou `uuid`.

{
  "data": {
    "id": "120250000000000003",
    "uuid": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "customer_integration_uuid": "5d7e8f9a-0b1c-4d2e-9f3a-4b5c6d7e8f90",
    "origin": "created",
    "synced_at": "2026-11-19T10:00:00-03:00",
    "deleted_at": null,
    "campaign_id": "120250000000000001",
    "adset_id": "120250000000000002",
    "name": "Black Friday, imagem",
    "status": "paused",
    "effective_status": "paused",
    "issues": [],
    "format": "image",
    "headline": "Black Friday",
    "body": "Até 50% off",
    "description": null,
    "link_url": "https://loja.com/bf",
    "call_to_action": "shop_now",
    "thumbnail_url": null,
    "facebook_ads": {
      "creative_id": "120250000000000004",
      "page_id": "1068595869664481",
      "instagram_account_id": null,
      "post_id": null
    },
    "created_at": "2026-11-19T10:00:00-0300",
    "updated_at": "2026-11-19T10:00:00-0300"
  }
}
PATCH/ads/{id}

Atualizar anúncio

Nome, status e textos, num corpo plano. Mudar um texto troca o criativo e manda o anúncio para revisão; num anúncio impulsionado, o criativo não pode ser trocado.

{
  "headline": "Última chance",
  "description": null
}
POST/ads/{id}/duplicate

Duplicar um anúncio

Copia um anúncio existente. A cópia nasce pausada, mesmo que o original esteja ativo.

{
  "name": "Black Friday, cópia"
}
DELETE/ads/{id}

Remover anúncio

Remove apenas o anúncio; o conjunto e a campanha continuam de pé.

{
  "data": {
    "id": "120250000000000003",
    "deleted": true
  }
}

Mídia do anúncio

Entregue o arquivo à conta de anúncios uma vez e reaproveite em quantos anúncios quiser, pelo `hash` da imagem ou pelo `id` do vídeo. É assim que um criativo com versão de feed e de story nomeia cada arquivo. O Connect não guarda nada: guarde você o `hash` ou o `id`.

POST/ads/facebook_ads/media

Entregar arquivo à conta

Manda à conta uma imagem (JPEG ou PNG) ou um vídeo pela `url`, um endereço https público, como o devolvido por `POST /media`. A imagem volta `ready` com o `hash`; o vídeo volta `processing` com o `id`. `name` é o nome na biblioteca da conta; sem ele, vale o nome do arquivo na URL. Exige `Idempotency-Key`.

{
  "type": "video",
  "url": "https://cdn.loja.com/bf-feed.mp4"
}
GET/ads/facebook_ads/videos/{id}

Processamento de um vídeo

Diz se um vídeo entregue está `processing`, `ready` ou `failed`. Use o `id` num anúncio só com `ready`. `thumbnail_url` é um quadro escolhido pela rede, que serve de miniatura no anúncio. Só responde por vídeos que esta conta recebeu por `POST /ads/facebook_ads/media`: qualquer outro `id` é 404.

{
  "data": {
    "id": "120210000000001",
    "status": "ready",
    "thumbnail_url": "https://scontent.xx.fbcdn.net/v/frame.jpg"
  }
}

Descoberta

Os ids que um anúncio precisa não são para o seu cliente copiar à mão do Gerenciador de Anúncios. Estes endpoints devolvem o que a conta de anúncios tem, já escopado nela. O que é só da Meta fica em `/ads/facebook_ads/*`.

GET/ads/facebook_ads/pages

Páginas

As páginas do Facebook que esta conta de anúncios pode usar para assinar um anúncio.

{
  "data": [
    { "id": "1068595869664481", "name": "Loja Exemplo", "picture_url": "https://…", "can_post": true,
      "instagram_account_id": "17841442920331135" }
  ]
}
GET/ads/facebook_ads/instagram-accounts

Contas do Instagram

As contas do Instagram vinculadas à conta de anúncios.

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  "https://connect.reportei.com/api/ads/facebook_ads/instagram-accounts?customer_integration=$CONNECTION_UUID"
GET/ads/facebook_ads/posts

Posts para impulsionar

O conteúdo orgânico que pode ser impulsionado. Por padrão vem do Instagram, que é o que o impulsionamento consome.

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  "https://connect.reportei.com/api/ads/facebook_ads/posts?customer_integration=$CONNECTION_UUID&source=instagram&instagram_account_id=17841442920331135"
GET/ads/facebook_ads/pixels

Pixels

Os pixels da conta, para medir conversão.

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  "https://connect.reportei.com/api/ads/facebook_ads/pixels?customer_integration=$CONNECTION_UUID"
GET/ads/facebook_ads/pixels/{id}/events

Eventos de um pixel

Os eventos que um pixel já registrou, com a contagem de cada um.

{
  "data": [
    { "event_name": "Purchase", "count": 1823 },
    { "event_name": "AddToCart", "count": 9120 }
  ]
}
GET/ads/facebook_ads/custom-conversions

Conversões personalizadas

As conversões da conta. Uma conversão excluída é arquivada pela integração e não aparece aqui.

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  "https://connect.reportei.com/api/ads/facebook_ads/custom-conversions?customer_integration=$CONNECTION_UUID"
POST/ads/facebook_ads/custom-conversions

Criar conversão personalizada

A regra vem de você (`rule.url_contains` ou `rule.event`) e o `pixel_id` é obrigatório. Exige `Idempotency-Key`.

{
  "name": "Compra acima de R$ 200",
  "pixel_id": "1234567890",
  "event_type": "purchase",
  "rule": { "url_contains": "/checkout/sucesso" }
}
GET/ads/audiences

Públicos personalizados

Os públicos que a segmentação pode usar em `facebook_ads.targeting.custom_audiences`.

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  "https://connect.reportei.com/api/ads/audiences?customer_integration=$CONNECTION_UUID"
GET/ads/targeting/search

Buscar segmentação

Resolve um termo nos ids que a segmentação espera. Interesses e comportamentos voltam com `id`; lugares voltam com `key` e um `subtype` que diz se é país, região ou cidade.

{
  "data": [
    { "id": "6003139266461", "name": "Café", "type": "interest", "audience_size": { "lower": 10000000, "upper": 50000000 } },
    { "key": "BR", "name": "Brasil", "type": "geo", "subtype": "country" }
  ]
}

Erros desta superfície

Além dos erros comuns da API, estes são próprios dos endpoints de anúncios.

Statuserror_codeDescrição
403forbiddenA conexão informada não é deste cliente.
404ads_object_not_foundO objeto não está na conta de anúncios desta conexão, ou o `uuid` não é de um objeto que o Connect guarda. É 404 e não 403 de propósito: a resposta não confirma que o id existe em outro lugar.
409operation_in_progressA mesma `Idempotency-Key` ainda está rodando. Espere o `Retry-After` e repita.
409budget_level_mismatchO orçamento desta campanha vive no outro nível (`budget_level` diz qual). Mande-o lá.
409ads_video_not_readyA integração ainda está processando o vídeo. Repita com a mesma `Idempotency-Key`: o vídeo e os níveis já criados são reaproveitados.
422validation_errorFalha de regra do corpo. `errors` diz o campo e, para enums, `supported_values` diz o que era aceito.
422idempotency_key_requiredUma criação sem o header `Idempotency-Key`.
422idempotency_key_reusedA mesma `Idempotency-Key` com outro pedido.
422ads_connection_expiredO token da conexão precisa ser renovado pelo seu cliente (`integration_status: expired`).
422ads_unsupported_integrationA conexão existe, mas não é de uma integração de anúncios que gerenciamos.
422boosted_creative_not_replaceableO criativo de um anúncio impulsionado não pode ser trocado. Crie outro anúncio.
4xx/5xxads_integration_errorA integração recusou a operação, ou não respondeu.

Quando a integração recusa por regra de negócio, o status dela é espelhado e o `error_code` é o código dela, não `ads_integration_error`, com o detalhe em `integration_error`. Se ela não responder, o status é 502 e repetir com a mesma `Idempotency-Key` retoma de onde parou.

Mídia

Envie um arquivo uma vez e receba uma URL para usar em posts e anúncios. O Connect guarda o arquivo e devolve uma URL em file.connect.reportei.com, um domínio que as redes aceitam, inclusive as que só buscam mídia de domínio verificado. O módulo só guarda o arquivo e devolve a URL: não publica nada e não sabe para qual rede o arquivo vai. A URL não tem assinatura: quem tem o link lê o arquivo, então trate-a como segredo até publicar. Todas as rotas pedem o Bearer e o x-customer-token.

É opcional

Posts e anúncios continuam recebendo mídia por URL. Se você já hospeda o arquivo numa URL pública, continue mandando a sua URL. Use este módulo quando não tiver onde hospedar, ou quando a rede recusar o seu domínio.

Com a mídia pronta, use a url em media[].url de POST /posts e dos anúncios: Posts · Advertising

Envio único (até 8 MB)

Até 8 MB, uma requisição basta: POST /media com o arquivo em multipart/form-data, no campo file. A resposta já traz a mídia pronta e a url. Acima do teto, a resposta é 413 media_too_large_for_single_upload, e o arquivo vai em partes.

POST/media

Enviar um arquivo (até 8 MB)

O arquivo inteiro numa requisição multipart/form-data, no campo file. Responde 201 com a mídia ready e a url; a repetição com a mesma Idempotency-Key responde 200 com a mesma mídia. Acima de 8 MB, 413 media_too_large_for_single_upload.

Parâmetros

CampoDescrição
Idempotency-KeyHeader obrigatório, até 255 caracteres. Uma chave sua por arquivo: gere antes da primeira tentativa e reenvie a mesma em toda repetição.
fileObrigatório. O arquivo, até 8 MB. O tipo é lido do conteúdo, não do nome.
{
  "media": {
    "uuid": "8b1e4f2a-6c3d-4e5f-9a0b-1c2d3e4f5a6b",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "status": "ready",
    "type": "image",
    "mime_type": "image/jpeg",
    "name": "verao-4x5.jpg",
    "size_bytes": 482133,
    "url": "https://file.connect.reportei.com/media/2f0c.../8b1e....jpg",
    "expires_at": "2026-11-29T12:00:00.000000Z",
    "upload": null,
    "created_at": "2026-10-30T12:00:00.000000Z"
  }
}

Envio em partes (até 1 GB)

Acima de 8 MB, até 1 GB, o arquivo vai em partes. Serve também para arquivos pequenos. O envio fica aberto por 24 horas: o que não for completado nesse prazo é descartado.

1

Abra o envio com POST /media/uploads, com file_name e o size exato em bytes. A resposta traz a mídia em uploading e, em upload, o part_size (bytes de cada parte), parts (quantas são) e expires_at (até quando completar).

2

Envie cada parte com PUT /media/uploads/{uuid}/parts/{n}, numeradas a partir de 1: o conteúdo bruto no corpo (application/octet-stream) e o Content-Length exato. Cada parte tem part_size bytes, e a última leva o resto. A parte 1 vai primeiro, porque o tipo do arquivo é lido dela; depois, as outras vão em qualquer ordem, inclusive em paralelo.

3

Se o envio for interrompido, GET /media/uploads/{uuid} diz em received_parts quais partes já chegaram. Mande só as que faltam. Reenviar uma parte substitui a anterior.

4

Feche com POST /media/uploads/{uuid}/complete. O Connect junta as partes, confere o arquivo inteiro e responde 201 com a mídia ready e a url.

POST/media/uploads

Abrir um envio em partes

Para arquivos de até 1 GB. Responde 201 com a mídia em uploading e, em upload, part_size, parts e expires_at. Complete antes de upload.expires_at, 24 horas depois.

Parâmetros

CampoDescrição
Idempotency-KeyHeader obrigatório, até 255 caracteres. Uma chave sua por arquivo: gere antes da primeira tentativa e reenvie a mesma em toda repetição.
file_nameObrigatório, até 255 caracteres. O nome do arquivo, que volta em name.
sizeObrigatório. O tamanho exato do arquivo em bytes, de 1 a 1073741824 (1 GB). Define quantas partes o envio tem.
{
  "file_name": "reels-verao.mp4",
  "size": 52428800
}

Exemplo: as partes com split e curl

Corte o arquivo no part_size devolvido pelo POST /media/uploads e envie as partes em ordem. Com --data-binary, o curl manda o Content-Length certo.

# 50 MB in parts of upload.part_size (8388608 bytes): part_aa, part_ab...
split -b 8388608 reels-verao.mp4 part_

n=1
for part in part_*; do
  curl -s -X PUT "https://connect.reportei.com/api/media/uploads/$MEDIA_UUID/parts/$n" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "x-customer-token: $CUSTOMER_TOKEN" \
    -H "Content-Type: application/octet-stream" \
    --data-binary @"$part"
  n=$((n + 1))
done
PUT/media/uploads/{uuid}/parts/{part}

Enviar uma parte

O corpo é o conteúdo bruto da parte (application/octet-stream), com exatamente upload.part_size bytes, ou o resto na última, e Content-Length obrigatório. A parte 1 vai primeiro; depois, qualquer ordem, inclusive em paralelo. Reenviar uma parte substitui a anterior. Responde 204, sem corpo.

curl -s -i -X PUT https://connect.reportei.com/api/media/uploads/$MEDIA_UUID/parts/1 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "x-customer-token: $CUSTOMER_TOKEN" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @part_aa
GET/media/uploads/{uuid}

Estado de um envio em partes

A mídia e received_parts, as partes que já chegaram, para retomar um envio interrompido sem mandar tudo de novo. Depois da parte 1, type e mime_type já vêm preenchidos.

{
  "media": {
    "uuid": "3c9d2e1f-7a4b-4c5d-8e6f-0a1b2c3d4e5f",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "status": "uploading",
    "type": "video",
    "mime_type": "video/mp4",
    "name": "reels-verao.mp4",
    "size_bytes": 52428800,
    "url": null,
    "expires_at": null,
    "upload": {
      "part_size": 8388608,
      "parts": 7,
      "expires_at": "2026-10-31T12:00:00.000000Z"
    },
    "created_at": "2026-10-30T12:00:00.000000Z"
  },
  "received_parts": [1, 2, 4]
}
POST/media/uploads/{uuid}/complete

Completar o envio

Junta as partes, confere o arquivo inteiro e responde 201 com a mídia ready e a url. Não pede Idempotency-Key: completar de novo responde 200 com a mesma mídia. Com partes faltando, 409 media_parts_missing; um arquivo que não confere é apagado e responde 422 media_content_invalid.

{
  "media": {
    "uuid": "3c9d2e1f-7a4b-4c5d-8e6f-0a1b2c3d4e5f",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "status": "ready",
    "type": "video",
    "mime_type": "video/mp4",
    "name": "reels-verao.mp4",
    "size_bytes": 52428800,
    "url": "https://file.connect.reportei.com/media/2f0c.../3c9d....mp4",
    "expires_at": "2026-11-29T12:00:00.000000Z",
    "upload": null,
    "created_at": "2026-10-30T12:00:00.000000Z"
  }
}

A URL

A url só existe com status ready. Ela é a mesma em toda leitura até expires_at: guarde-a ou leia de novo por GET /media/{uuid}.

A mídia vale 30 dias a partir de ficar pronta. Em expires_at, a URL deixa de valer e o arquivo é apagado. Não há rota para apagar antes nem para estender o prazo: publique o post ou o anúncio dentro dele.

Consultar mídias

As mídias do cliente que ainda valem, da mais recente para a mais antiga. Uma mídia expirada responde 404 media_not_found.

GET/media

Listar mídias

As mídias do cliente que ainda valem, da mais recente para a mais antiga. Filtro status (uploading ou ready); per_page de 1 a 100, padrão 15.

{
  "data": [
    {
      "uuid": "8b1e4f2a-6c3d-4e5f-9a0b-1c2d3e4f5a6b",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "status": "ready",
      "type": "image",
      "mime_type": "image/jpeg",
      "name": "verao-4x5.jpg",
      "size_bytes": 482133,
      "url": "https://file.connect.reportei.com/media/2f0c.../8b1e....jpg",
      "expires_at": "2026-11-29T12:00:00.000000Z",
      "upload": null,
      "created_at": "2026-10-30T12:00:00.000000Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/media",
    "per_page": 15,
    "to": 1,
    "total": 1
  }
}
GET/media/{uuid}

Consultar uma mídia

Uma mídia do cliente e a url, que é a mesma em toda leitura até expires_at. Uma mídia expirada responde 404 media_not_found.

{
  "media": {
    "uuid": "8b1e4f2a-6c3d-4e5f-9a0b-1c2d3e4f5a6b",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "status": "ready",
    "type": "image",
    "mime_type": "image/jpeg",
    "name": "verao-4x5.jpg",
    "size_bytes": 482133,
    "url": "https://file.connect.reportei.com/media/2f0c.../8b1e....jpg",
    "expires_at": "2026-11-29T12:00:00.000000Z",
    "upload": null,
    "created_at": "2026-10-30T12:00:00.000000Z"
  }
}

Mídias de todos os clientes

Para ver as mídias de todos os seus clientes, só com o Bearer, use a rota de panorama na seção Merchants:

GET/merchants/media

Sem duplicar

POST /media e POST /media/uploads exigem o header Idempotency-Key. Mesma chave e mesmo pedido: 200 com a mídia já guardada e o header Idempotent-Replay: true, sem guardar de novo. Mesma chave com outro pedido: 422 idempotency_key_reused. A chave de uma mídia que já expirou responde 404 media_not_found: use outra. O complete não pede chave, porque completar de novo responde 200 com a mesma mídia.

Tipos e limites

  • Tipos aceitos: JPEG, PNG, WebP e GIF (image), MP4 e MOV (video). O tipo é lido dos bytes do arquivo; o nome e a extensão não decidem nada. O arquivo inteiro é conferido antes de ficar pronto.
  • Envio único: até 8 MB.
  • Envio em partes: até 1 GB.
  • Imagem: até 30 MB.
  • Até 20 envios em partes abertos ao mesmo tempo por cliente.
  • Um volume diário de envio por conta. Acima dele, 429 media_daily_limit.
  • Um envio em partes precisa ser completado em 24 horas.
  • A mídia pronta vale 30 dias.
  • Os limites de cada rede (tamanho, duração, proporção) não são conferidos aqui: quem recusa é a rede, no post ou no anúncio.

Erros

Além dos erros comuns da API, estas são as recusas das rotas de mídia, com message e error_code. Ramifique por error_code, nunca pela mensagem. Uma falha de validação do corpo ou da query devolve 422 com errors.

Statuserror_codeDescrição
404media_not_foundNão há mídia com esse uuid neste cliente, ou ela já expirou. Também no POST com a Idempotency-Key de uma mídia expirada: use outra chave.
409media_upload_in_progressUm POST /media com a mesma Idempotency-Key ainda está guardando o arquivo. Repita em instantes.
409media_part_one_firstOutra parte chegou antes da parte 1. Envie a parte 1 primeiro.
409media_already_completedParte enviada para um envio já completado.
409media_parts_missingO complete foi chamado com partes faltando, listadas na mensagem. Envie as que faltam e complete de novo.
411media_part_length_requiredParte sem Content-Length.
413media_too_large_for_single_uploadO arquivo passa do teto do envio único. Mande em partes por POST /media/uploads.
422media_type_not_supportedO arquivo não é JPEG, PNG, WebP, GIF, MP4 nem MOV. Nada foi guardado.
422media_content_invalidO arquivo não é uma imagem ou um vídeo íntegro do tipo que os primeiros bytes declaram. Nada foi guardado.
422media_type_changedA parte 1 foi reenviada com um arquivo de outro tipo. Para outro arquivo, abra um envio novo.
422media_part_out_of_rangeO número da parte está fora de 1 a parts.
422media_part_size_mismatchA parte não tem part_size bytes, ou o resto, na última.
422media_image_too_largeImagem acima de 30 MB.
422idempotency_key_reusedA Idempotency-Key já foi usada com outro pedido.
429media_too_many_uploadsO cliente já tem 20 envios em partes abertos. Complete algum ou espere expirar.
429media_daily_limitO volume diário de envio da conta foi atingido. Tente de novo amanhã.

Posts

Publique posts nas contas que seus customers já conectaram ao Reportei Connect. Você envia o conteúdo e o Connect cuida de todo o processo com a rede: envia as mídias, espera o processamento, publica e executa as ações pós-publicação.

A conta de destino

Toda rota de posts diz em qual conta age de uma de duas formas, nunca as duas. Pela conexão: customer_integration na query, o mesmo uuid de GET /customer-integrations e das rotas de Advertising. Pelo nome: integration_slug e source_name no corpo (nas rotas de Pinterest e YouTube, só source_name na query), que continua valendo quando a conexão é apagada e refeita para a mesma conta. Mandar os dois é recusado com 422. Os exemplos desta seção usam a conexão.

Como funciona

  1. Você envia o post com POST /posts, informando a conta de destino (customer_integration na query, ou integration_slug + source_name no corpo), o formato e as mídias.
  2. O Connect valida o pedido, grava o post e responde 202 na hora, com o uuid e o status pending. A publicação ainda não aconteceu.
  3. Em segundo plano, o Connect envia as mídias para a rede, espera o processamento (vídeos podem levar alguns minutos) e publica.
  4. Você acompanha o resultado com GET /posts/{uuid} até o post chegar a um status final: published, partially_published ou failed.

Redes

O endpoint POST /posts é o mesmo para todas as redes: a conexão (ou o integration_slug) define a rede de destino. Cada rede tem a sua seção, com formatos, campos aceitos, códigos de erro e exemplos.

InstagramFacebookThreadsLinkedInPinterestYouTube

URL base: https://connect.reportei.com/api

Autenticação e Idempotência

As rotas de posts usam os mesmos headers do restante da API. O POST exige também uma chave de idempotência.

HeaderDescrição
AuthorizationBearer token do merchant. Obrigatório em todas as rotas.
x-customer-tokenToken do customer dono da conta de destino. Define em nome de quem o post é publicado e quais posts você pode consultar. Obrigatório em todas as rotas. Sem ele, a rota responde 401.
Idempotency-KeyObrigatório só no POST /posts, até 255 caracteres. Identifica a tentativa de envio: reenviar com a mesma chave nunca publica duas vezes. A chave não expira e é única por customer.

Idempotência e duplicidade

Duas travas protegem contra posts duplicados: a Idempotency-Key protege o reenvio da mesma requisição, e a external_reference impede publicar o mesmo conteúdo duas vezes na mesma conta.

SituaçãoResposta
Chave nova.202 Accepted com o post criado.
Mesma chave e mesmo corpo, por exemplo num retry após timeout.200 OK com o estado atual do post e o header Idempotent-Replay: true. Nada é publicado de novo.
Mesma chave com corpo diferente.422 com error_code idempotency_key_reused.
Chave nova, mas já existe uma publicação em andamento para a mesma conta e external_reference.409 com error_code post_in_progress e o post_uuid da publicação existente.
Chave nova, mas a mesma conta e external_reference já foram publicadas.409 com error_code post_already_published e o post_uuid.
Chave nova, e as tentativas anteriores para a mesma conta e external_reference falharam.202: a nova tentativa é aceita. É assim que se reenvia um post que falhou.

Como tentar de novo

Para repetir uma requisição que ficou sem resposta (timeout, queda de rede), reenvie o mesmo corpo com a mesma Idempotency-Key. Para tentar de novo um post que terminou em failed, corrija o problema e envie com uma Idempotency-Key nova, mantendo a external_reference.

Instagram

Publique em contas conectadas como instagram (login do Instagram) ou instagram_business (conta conectada pelo Facebook). Algumas opções só funcionam em instagram_business e estão sinalizadas abaixo.

Formatos suportados

  • feed: uma imagem.
  • carrossel: feed com 2 a 10 mídias, imagens e vídeos misturados.
  • reels: exatamente um vídeo, com capa, música, trial reels e as demais opções.
  • story: 1 a 10 mídias, cada uma publicada como um story separado, na ordem enviada.

Criar post no Instagram

Grava o post e coloca a publicação na fila. A resposta sai em seguida com status 202; o resultado da publicação é consultado depois.

POST/posts

Criar post

Valida o pedido, grava o post e coloca a publicação na fila. Responde 202 com o post em pending, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "post-2026-10-08-001",
  "format": "feed",
  "caption": "Coleção de verão com @parceiro_oficial #verao",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/colecao.jpg",
      "alt_text": "Modelo usando a coleção de verão",
      "user_tags": [
        { "username": "maria.silva", "x": 0.1667, "y": 0.2778 },
        { "username": "@Loja.Centro" }
      ]
    }
  ],
  "instagram": {
    "collaborators": ["parceiro_oficial"],
    "location_id": "106377336067638",
    "first_comment": "Link na bio!",
    "comments_disabled": false
  }
}

Corpo da requisição

CampoDescrição
customer_integrationNa query, não no corpo. uuid da conexão de destino, de GET /customer-integrations. Com ele, não envie integration_slug nem source_name.
integration_slugObrigatório sem customer_integration. instagram ou instagram_business. Os dois buscam a conta pelo source_name tanto entre as conectadas pelo login do Instagram quanto entre as conectadas pelo Facebook, e a resposta traz em integration_slug o tipo de conexão encontrado.
source_nameObrigatório sem customer_integration. Username da conta de destino, como aparece na integração do customer. A conta precisa estar conectada e ativa.
external_referenceObrigatório, até 100 caracteres. O identificador do post no seu sistema. Impede publicar o mesmo post duas vezes na mesma conta e permite listar as tentativas com GET /posts.
formatObrigatório. feed, reels ou story. Feed com 2 ou mais mídias vira carrossel.
captionOpcional, até 2.200 caracteres, com no máximo 30 hashtags e 20 menções (@). Não é aceita em story.
mediaObrigatório, lista de 1 a 10 mídias. A ordem da lista é a ordem de publicação.
media.*.typeObrigatório. image ou video.
media.*.urlObrigatório. URL https pública, até 2.048 caracteres, de onde o Instagram baixa o arquivo. URLs assinadas precisam continuar válidas até a publicação terminar. Sem onde hospedar o arquivo? Envie pela seção Mídia (POST /media) e use a url que ela devolve.
media.*.alt_textOpcional, até 1.000 caracteres. Texto alternativo da imagem, enviado no feed e nas imagens do carrossel.
media.*.user_tagsOpcional. Pessoas marcadas nessa mídia: lista de { username, x, y }. Veja as regras em Marcação de pessoas.
instagramOpcional. Opções específicas do Instagram. Cada formato aceita um conjunto de campos; qualquer outro é recusado com 422.

Regras por formato

formatmediacaptioninstagram
feed1 imagem, ou de 2 a 10 mídias (imagens e vídeos) para carrosselSimcollaborators, location_id, first_comment, comments_disabled
reelsExatamente 1 vídeoSimcollaborators, location_id, share_to_feed, cover_url, audio_id, audio_volume, video_volume, trial_graduation_strategy, first_comment, comments_disabled
story1 a 10 mídias, imagem ou vídeoNãocollaborators

Feed com um único vídeo é recusado: o Instagram só publica vídeo único como reels, então use format reels.

Opções do Instagram

CampoDescrição
collaboratorsLista de até 5 usernames convidados como colaboradores. O post aparece também no perfil de quem aceitar o convite.
location_idID de um local do Facebook (página com endereço) para marcar a localização.
share_to_feedReels: exibe o reels também no feed do perfil. Padrão true.
cover_urlReels: URL https da imagem de capa.
audio_idReels: ID de uma música da biblioteca do Instagram.
audio_volumeReels: volume da música, de 0 a 100. Exige audio_id.
video_volumeReels: volume do som original do vídeo, de 0 a 100. Exige audio_id.
trial_graduation_strategyReels: publica como trial reel, mostrado primeiro para quem não segue a conta. MANUAL ou SS_PERFORMANCE (promove automaticamente se tiver bom desempenho).
first_commentAté 2.200 caracteres. Publicado como primeiro comentário logo depois do post.
comments_disabledtrue desativa os comentários do post depois da publicação. Padrão false.

Música só em contas instagram_business

A biblioteca de músicas do Instagram só está disponível para contas conectadas como instagram_business. Em contas instagram, o Instagram publica o reels sem a música e não devolve erro.

Marcação de pessoas (user_tags)

A marcação é feita por mídia: em carrossel e story, cada arquivo tem as suas próprias tags.

  • Imagem: x e y definem a posição da marcação, de 0 a 1 a partir do canto superior esquerdo. Informe os dois ou nenhum; sem posição, a marcação vai para o centro (0.5, 0.5).
  • Vídeo (reels e vídeos de story): só o username. Enviar x ou y é recusado. Em vídeos de carrossel, as tags são ignoradas.
  • Carrossel em conta instagram (login do Instagram): tags não são aceitas e a requisição é recusada com 422 e error_code carousel_tags_not_supported. Em instagram_business, as tags vão nas imagens.
  • O username é normalizado: o @ inicial é removido e tudo vira minúsculo. @Maria.Silva é gravado como maria.silva. O mesmo vale para collaborators.
  • Usernames aceitam apenas letras, números, ponto e underline, até 30 caracteres.

Convertendo uma grade de posições

Se a sua interface posiciona a marcação numa grade, envie o centro da célula: x = (coluna + 0,5) / colunas e y = (linha + 0,5) / linhas, com coluna e linha começando em 0. Numa grade de 3 colunas por 9 linhas, a célula da primeira coluna na terceira linha vira x 0.1667 e y 0.2778.

Erros do Instagram

Além dos códigos gerais, a publicação no Instagram pode falhar com os códigos abaixo em publishing.error.code. Os numéricos repetem o código de erro da Graph API (code ou code/subcode), e message repete a mensagem original, em inglês. Os mais comuns:

error.codeDescrição
ERRORO Instagram recusou a mídia ao processá-la (formato, duração, proporção). message traz o motivo informado pelo Instagram.
EXPIREDO container de mídia expirou antes de ser publicado.
container_missingNão havia container para publicar. Envie de novo com uma Idempotency-Key nova.
190Token de acesso inválido ou expirado. A integração é marcada como expirada.
36000Imagem maior que o tamanho permitido.
36001Proporção da mídia não suportada.
36004Legenda maior que o permitido.
9004Mais hashtags que o permitido.
9004/2207052O Instagram não conseguiu baixar a mídia: confira se a URL é pública e continua válida.
10/2207081A conta não pode publicar trial reels.
110/2207018Não é possível marcar ou acessar um usuário privado.
9007A mídia já foi publicada.

Token expirado

Um erro de token (190) também marca a integração do customer como expirada, pelo mesmo fluxo do restante da API, e os webhooks de integração são disparados normalmente. Novos posts para essa conta são recusados com integration_inactive até o customer reconectar.

Exemplos por Formato

Corpos completos de POST /posts para cada formato do Instagram. O exemplo de feed com uma imagem está em Criar post no Instagram.

Feed com 3 mídias: imagens e vídeo misturados, marcação na primeira imagem, colaborador e primeiro comentário.

{
  "external_reference": "carousel-001",
  "format": "feed",
  "caption": "Bastidores da sessão de fotos",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/1.jpg",
      "alt_text": "Equipe no estúdio",
      "user_tags": [{ "username": "joao_fotografo", "x": 0.5, "y": 0.8333 }]
    },
    { "type": "video", "url": "https://cdn.example.com/posts/2.mp4" },
    { "type": "image", "url": "https://cdn.example.com/posts/3.jpg" }
  ],
  "instagram": {
    "collaborators": ["joao_fotografo"],
    "first_comment": "Qual foto você prefere?"
  }
}

Facebook

Publique em Páginas do Facebook conectadas pelo customer, com integration_slug facebook. Perfis pessoais e grupos não são suportados.

Formatos suportados

  • feed: post de texto, de link ou com 1 a 10 imagens.
  • reels: exatamente um vídeo, com título opcional.
  • story: 1 a 10 mídias, cada uma publicada como um story separado, na ordem enviada.

Criar post no Facebook

Grava o post e coloca a publicação na fila. A resposta sai em seguida com status 202; o resultado da publicação é consultado depois.

POST/posts

Criar post no Facebook

Valida o pedido, grava o post e coloca a publicação na fila. Responde 202 com o post em pending, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "fb-post-2026-10-09-001",
  "format": "feed",
  "caption": "Coleção de verão chegando às lojas",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/colecao.jpg",
      "alt_text": "Modelo usando a coleção de verão"
    }
  ]
}

Corpo da requisição

CampoDescrição
customer_integrationNa query, não no corpo. uuid da conexão da Página, de GET /customer-integrations. Com ele, não envie integration_slug nem source_name.
integration_slugObrigatório sem customer_integration. facebook.
source_nameObrigatório sem customer_integration. Nome da Página de destino, como aparece na integração do customer. A Página precisa estar conectada e ativa.
external_referenceObrigatório, até 100 caracteres. O identificador do post no seu sistema. Impede publicar o mesmo post duas vezes na mesma Página e permite listar as tentativas com GET /posts.
formatObrigatório. feed, reels ou story.
captionOpcional, até 2.200 caracteres. No feed é o texto do post; no reels, a descrição do vídeo. Obrigatória no feed sem mídia e sem link. Não é aceita em story.
mediaLista de até 10 mídias, na ordem de publicação. Obrigatória em reels e story; opcional no feed.
media.*.typeObrigatório. image ou video.
media.*.urlObrigatório. URL https pública, até 2.048 caracteres, de onde o Facebook baixa o arquivo. URLs assinadas precisam continuar válidas até a publicação terminar. Sem onde hospedar o arquivo? Envie pela seção Mídia (POST /media) e use a url que ela devolve.
media.*.alt_textOpcional, até 1.000 caracteres. Texto alternativo das imagens, enviado no feed e nos stories.
facebookOpcional. Opções específicas do Facebook. Cada formato aceita um conjunto de campos; qualquer outro é recusado com 422.

Regras por formato

formatmediacaptionfacebook
feedNenhuma (post de texto ou de link), ou de 1 a 10 imagensSim. Obrigatória sem mídia e sem linklink
reelsExatamente 1 vídeoSim, vira a descrição do vídeotitle
story1 a 10 mídias, imagem ou vídeoNão

Vídeo no feed é recusado: no Facebook, vídeo de Página é publicado como reel, então use format reels. Um post com link não leva mídia, porque o Facebook descarta as imagens de um post com link.

Opções do Facebook

CampoDescrição
linkFeed: URL http ou https, até 2.048 caracteres, publicada com prévia do link. Não pode ser combinada com media.
titleReels: título do vídeo, até 255 caracteres.

O que o Facebook não aceita

Posts do Facebook não aceitam user_tags nem o bloco instagram: os dois são recusados com 422. Também não há primeiro comentário, colaboradores, localização nem desativação de comentários.

Erros do Facebook

Além dos códigos gerais, a publicação no Facebook pode falhar com os códigos abaixo em publishing.error.code. Os numéricos repetem o código de erro da Graph API (code ou code/subcode), e message repete a mensagem original, em inglês. Limites de taxa (4, 17, 32 e 341) e instabilidades (1 e 2) são tentados de novo automaticamente; se persistirem, o post falha com o próprio código. Os mais comuns:

error.codeDescrição
container_errorO Facebook recusou o vídeo ao recebê-lo ou processá-lo (formato, duração, arquivo corrompido). message traz o motivo informado pelo Facebook.
container_missingNão havia post preparado para publicar. Envie de novo com uma Idempotency-Key nova.
190Token de acesso inválido ou expirado, com ou sem subcode (190/463, por exemplo). A integração é marcada como expirada. A exceção é 190/459: a conta está bloqueada para verificação de segurança, e o dono precisa entrar no Facebook para liberá-la.
10Sem permissão para publicar na Página. Peça ao customer para reconectar a conta concedendo todas as permissões.
10/4854002O Facebook exige que o administrador da Página confirme a identidade antes de publicar. O mesmo vale para 368/4854002.
200Permissão insuficiente: o usuário que conectou a Página não tem a função necessária para publicar nela.
100O Facebook recusou algum dado do post, como o link ou a URL da mídia. message traz o detalhe.
368O conteúdo foi bloqueado pelas políticas do Facebook, ou a Página está temporariamente impedida de publicar.
368/1404112A Página está com acesso limitado pelo Facebook.
506Post duplicado: o Facebook recusou um conteúdo igual a um publicado recentemente na Página.
1609005Formato de vídeo não suportado.
1609006Vídeo maior que o permitido. O Facebook também pode responder 350 para o mesmo caso.
352O Facebook não aceitou o arquivo de vídeo enviado.

Token expirado

Um erro de token (190 ou 102) também marca a integração do customer como expirada, pelo mesmo fluxo do restante da API, e os webhooks de integração são disparados normalmente. Novos posts para essa Página são recusados com integration_inactive até o customer reconectar.

Exemplos por Formato

Corpos completos de POST /posts para cada tipo de post no Facebook. O exemplo de feed com uma imagem está em Criar post no Facebook.

Post só com texto. Sem media e sem link, a caption é obrigatória.

{
  "external_reference": "fb-text-001",
  "format": "feed",
  "caption": "Amanhã a loja abre às 10h. Te esperamos!"
}

Threads

Publique em contas do Threads conectadas pelo customer, com integration_slug threads. O Threads só tem feed.

Formatos suportados

  • texto: só a caption, com link_attachment opcional para mostrar a prévia de um link.
  • mídia: 1 imagem ou 1 vídeo, com caption opcional.
  • carrossel: de 2 a 20 mídias, imagens e vídeos misturados.
POST/posts

Criar post no Threads

Valida o pedido, grava o post e coloca a publicação na fila. Responde 202 com o post em created, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "threads-001",
  "format": "feed",
  "caption": "Qual peça da coleção vocês querem ver primeiro? #verao",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/colecao-1.jpg",
      "alt_text": "Vestido da coleção de verão"
    },
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/colecao-2.jpg"
    }
  ],
  "threads": {
    "reply_control": "accounts_you_follow",
    "topic_tag": "Moda",
    "first_comment": "Link da loja na bio"
  }
}

Regras por formato

formatmediacaptionthreads
feedNenhuma (texto), 1 mídia, ou de 2 a 20 mídias em carrosselAté 500 caracteres. Obrigatória num post sem mídiareply_control, link_attachment, topic_tag, first_comment

Um post com link_attachment não leva mídia. user_tags não é aceito no Threads.

Opções do Threads

CampoDescrição
reply_controlQuem pode responder: everyone, accounts_you_follow, mentioned_only, parent_post_author_only ou followers_only.
link_attachmentURL mostrada com prévia de link. Só em post de texto, sem mídia.
topic_tagTópico do post, até 50 caracteres, sem ponto e sem &.
first_commentResposta publicada logo depois do post, até 500 caracteres. Se ela falhar, o post continua publicado.

Erros do Threads

Os numéricos repetem o código de erro da API do Threads (code ou code/subcode), e message repete a mensagem original, em inglês. Instabilidade (2) e limite de taxa (4) são tentados de novo automaticamente antes de o post falhar. Os mais comuns:

error.codeDescrição
190Token de acesso inválido ou expirado. A integração é marcada como expirada.
10A conta não tem permissão para publicar. O mesmo vale para 200 e 403.
100Parâmetro inválido. message traz o campo que o Threads recusou.
368O Threads bloqueou o conteúdo por violar as regras da rede.
36000Mídia maior que o tamanho permitido.
36001Proporção da mídia não suportada.
36001/2207084Imagem em WebP, formato que o Threads não aceita.
36004Texto maior que o permitido.

Exemplos por Formato

Post só de texto com prévia de link, aberto a respostas de todos. Sem mídia.

{
  "external_reference": "threads-text-001",
  "format": "feed",
  "caption": "Saiu o guia de tendências do verão",
  "threads": {
    "link_attachment": "https://www.example.com/blog/tendencias-verao",
    "reply_control": "everyone"
  }
}

LinkedIn

Publique em Páginas de empresa (integration_slug linkedin) ou em perfis pessoais (linkedin_personal). O LinkedIn só tem feed.

Formatos suportados

  • texto: só a caption.
  • imagens: de 1 a 20 imagens num post só.
  • vídeo: exatamente 1 vídeo, com título opcional em linkedin.title.
  • documento: exatamente 1 documento (PDF, PPT ou DOC), com título obrigatório em linkedin.title.
POST/posts

Criar post no LinkedIn

Valida o pedido, grava o post e coloca a publicação na fila. Responde 202 com o post em created, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "linkedin-001",
  "format": "feed",
  "caption": "Bastidores do nosso evento de lançamento",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/evento-1.jpg",
      "alt_text": "Palco do evento"
    },
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/evento-2.jpg"
    }
  ]
}

Regras por formato

formatmediacaptionlinkedin
feedNenhuma (texto), de 1 a 20 imagens, 1 vídeo ou 1 documentoAté 3.000 caracteres. Obrigatória num post sem mídiatitle

Vídeo e documento vão sozinhos, sem outras mídias. media.*.type document só é aceito no LinkedIn. user_tags não é aceito.

Opções do LinkedIn

CampoDescrição
titleTítulo do vídeo ou do documento, até 255 caracteres. Obrigatório em documento. Não é aceito em post de texto ou de imagens.

Erros do LinkedIn

Fora os códigos abaixo, error.code traz o code que o LinkedIn devolveu, e message repete a mensagem original, em inglês. Um 401 do LinkedIn é erro de token: a integração é marcada como expirada. Limite de taxa e instabilidade (409, 429 e 5xx) são tentados de novo automaticamente.

error.codeDescrição
PROCESSING_FAILEDO LinkedIn não conseguiu processar a mídia. message traz o motivo informado pelo LinkedIn.
upload_failedO arquivo não chegou ao LinkedIn depois de várias tentativas de envio.
media_size_unknownA URL da mídia não informou o tamanho do arquivo (Content-Length), que o envio de vídeo exige. Sirva o arquivo com Content-Length.
media_unreachableA URL da mídia não respondeu. É tentado de novo; o post falha se a URL continuar fora do ar.

Exemplos por Formato

Post só com texto.

{
  "external_reference": "linkedin-text-001",
  "format": "feed",
  "caption": "Estamos contratando! Vagas abertas em tecnologia."
}

Pinterest

Publique pins em contas do Pinterest conectadas pelo customer, com integration_slug pinterest. Todo pin vai para um board.

Formatos suportados

  • imagem: 1 imagem, com título, link e alt_text (até 500 caracteres).
  • vídeo: 1 vídeo, com capa obrigatória em pinterest.cover_url.
POST/posts

Criar pin no Pinterest

Valida o pedido, grava o pin e coloca a publicação na fila. Responde 202 com o post em created, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "pin-001",
  "format": "feed",
  "caption": "Paleta de cores da coleção de verão",
  "media": [
    {
      "type": "image",
      "url": "https://cdn.example.com/posts/paleta.jpg",
      "alt_text": "Tecidos em tons de areia"
    }
  ],
  "pinterest": {
    "board_id": "1094937759896573345",
    "title": "Coleção de verão",
    "link": "https://www.example.com/colecao-verao"
  }
}

Regras por formato

formatmediacaptionpinterest
feedExatamente 1 imagem ou 1 vídeoAté 800 caracteres (a descrição do pin)board_id, board_section_id, title, link, cover_url

pinterest.board_id é obrigatório. Os ids de board e de seção vêm das rotas de boards abaixo. user_tags não é aceito.

Opções do Pinterest

CampoDescrição
board_idObrigatório. id numérico do board de destino, de GET /posts/pinterest/boards.
board_section_idOpcional. id numérico da seção dentro do board, de GET /posts/pinterest/boards/{board_id}/sections.
titleTítulo do pin, até 100 caracteres.
linkURL de destino do pin.
cover_urlCapa do pin de vídeo, https. Obrigatória em vídeo e recusada em imagem.

Boards e seções

Leituras e criação ao vivo na conta do Pinterest, sem nada guardado no Connect. Recebem a conta pela conexão (customer_integration) ou pelo nome (source_name), na query ou, ao criar um board, no corpo.

GET/posts/pinterest/boards

Listar boards

Lista os boards da conta do Pinterest, lidos ao vivo na rede. Use o id em pinterest.board_id.

{
  "boards": [
    {
      "id": "1094937759896573345",
      "name": "Coleção de verão",
      "description": "Peças e paletas da estação",
      "privacy": "PUBLIC"
    },
    {
      "id": "1094937759896573400",
      "name": "Rascunhos",
      "description": null,
      "privacy": "SECRET"
    }
  ]
}
POST/posts/pinterest/boards

Criar board

Cria um board na conta do Pinterest e devolve o board criado com 201. name é obrigatório (até 180 caracteres); description (até 500) e privacy (PUBLIC, PROTECTED ou SECRET) são opcionais. Nada é guardado no Connect.

{
  "name": "Coleção de inverno",
  "description": "Peças da próxima estação",
  "privacy": "PUBLIC"
}
GET/posts/pinterest/boards/{board_id}/sections

Listar seções do board

Lista as seções de um board, lidas ao vivo na rede. Use o id em pinterest.board_section_id. Board que não existe na conta responde 404 board_not_found.

{
  "sections": [
    {
      "id": "5170127316372340412",
      "name": "Looks"
    }
  ]
}

Erros do Pinterest

Fora os códigos abaixo, error.code traz o code que o Pinterest devolveu, e message repete a mensagem original, em inglês. Limite de taxa e instabilidade (429 e 5xx) são tentados de novo automaticamente.

error.codeDescrição
2Token de acesso inválido ou expirado. A integração é marcada como expirada.
board_not_foundO board ou a seção não existe na conta.
permission_deniedA conta não tem permissão para a operação, por exemplo num board de outro usuário.
media_missingO pin não tinha mídia para publicar.

Exemplos por Formato

Pin de vídeo numa seção do board, com a capa obrigatória.

{
  "external_reference": "pin-video-001",
  "format": "feed",
  "caption": "Como montar um look de verão",
  "media": [
    {
      "type": "video",
      "url": "https://cdn.example.com/posts/look.mp4"
    }
  ],
  "pinterest": {
    "board_id": "1094937759896573345",
    "board_section_id": "5170127316372340412",
    "title": "Look de verão",
    "cover_url": "https://cdn.example.com/posts/look-capa.jpg"
  }
}

YouTube

Publique Shorts em canais do YouTube conectados pelo customer, com integration_slug youtube.

Formatos suportados

  • shorts: exatamente 1 vídeo. O YouTube decide se é Short pelo próprio vídeo (vertical, até 3 minutos); o format só declara a intenção.
POST/posts

Publicar Short no YouTube

Valida o pedido, grava o post e coloca a publicação na fila. Responde 202 com o post em created, ou 200 com o header Idempotent-Replay: true quando a mesma requisição é reenviada.

{
  "external_reference": "short-001",
  "format": "shorts",
  "caption": "Três jeitos de usar a mesma camisa",
  "media": [
    {
      "type": "video",
      "url": "https://cdn.example.com/posts/camisa.mp4"
    }
  ],
  "youtube": {
    "title": "Três jeitos de usar a mesma camisa",
    "privacy_status": "public",
    "category_id": "26",
    "tags": [
      "moda",
      "estilo",
      "verão"
    ],
    "made_for_kids": false,
    "notify_subscribers": true
  }
}

Regras por formato

formatmediacaptionyoutube
shortsExatamente 1 vídeo, sem alt_textA descrição do vídeo, até 5.000 bytes, sem < e >title, privacy_status, category_id, tags, made_for_kids, notify_subscribers

youtube.title e youtube.privacy_status são obrigatórios. user_tags não é aceito.

Opções do YouTube

CampoDescrição
titleObrigatório. Título do vídeo, até 100 caracteres, sem < e >.
privacy_statusObrigatório. public, unlisted ou private.
category_idid numérico da categoria, de GET /posts/youtube/categories.
tagsLista de tags, sem <, > e vírgulas. Juntas, até 500 caracteres contados como o YouTube mostra: separadas por vírgula, e uma tag com espaço entre aspas.
made_for_kidsSe o vídeo é feito para crianças.
notify_subscribersSe os inscritos são avisados da publicação.

Categorias

Leitura ao vivo das categorias do YouTube, cacheada por região e idioma. Recebe a conta pela conexão (customer_integration) ou pelo nome (source_name), na query.

GET/posts/youtube/categories

Listar categorias

Lista as categorias que um vídeo pode receber na região de region_code (padrão BR), com os títulos no idioma de hl. Use o id em youtube.category_id.

{
  "categories": [
    {
      "id": "22",
      "title": "Pessoas e blogs"
    },
    {
      "id": "26",
      "title": "Guias e estilo"
    }
  ]
}

Erros do YouTube

error.code traz o reason que o Google devolveu, e message repete a mensagem original, em inglês. Um 401 é erro de token: a integração é marcada como expirada. rateLimitExceeded, userRateLimitExceeded e backendError são tentados de novo automaticamente. Os mais comuns:

error.codeDescrição
quotaExceededA cota diária da API do YouTube acabou. Ela renova à meia-noite do horário do Pacífico: envie de novo depois, com outra Idempotency-Key.
uploadLimitExceededO canal atingiu o limite de envios do YouTube.
forbiddenA conta não tem permissão para enviar vídeos ao canal.

Exemplos por Formato

O mínimo para publicar: o vídeo, o título e a privacidade. Sem descrição.

{
  "external_reference": "short-unlisted-001",
  "format": "shorts",
  "media": [
    {
      "type": "video",
      "url": "https://cdn.example.com/posts/previa.mp4"
    }
  ],
  "youtube": {
    "title": "Prévia da coleção",
    "privacy_status": "unlisted"
  }
}

Consultar Posts

Consulte um post pelo uuid ou liste os posts do customer, com filtros opcionais por conexão (customer_integration), external_reference e status. As duas rotas só enxergam posts do customer do x-customer-token; sem o header, respondem 401. Os posts de todos os clientes ficam em GET /merchants/posts, na seção Merchants.

GET/posts/{uuid}

Consultar post

Retorna o post com o estado atual da publicação. Use para acompanhar o post até um status final.

{
  "post": {
    "uuid": "9f1c2b7e-1d3a-4c55-9a8e-2f6b0c1d4e5f",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "external_reference": "post-2026-10-08-001",
    "integration_slug": "instagram_business",
    "source_id": "17841400000000000",
    "source_name": "minha_marca",
    "format": "feed",
    "caption": "Coleção de verão com @parceiro_oficial #verao",
    "status": "published",
    "permalink": "https://www.instagram.com/p/DePI16BER0c/",
    "publishing": {
      "status": "published",
      "external_id": "18393642439204130",
      "error": null,
      "published_at": "2026-10-08 14:00:42"
    },
    "media": [
      {
        "position": 0,
        "type": "image",
        "url": "https://cdn.example.com/posts/colecao.jpg",
        "alt_text": "Modelo usando a coleção de verão",
        "user_tags": [
          { "username": "maria.silva", "x": 0.1667, "y": 0.2778 },
          { "username": "loja.centro" }
        ],
        "publishing": { "status": "published", "external_id": null, "error": null }
      }
    ],
    "instagram": {
      "collaborators": ["parceiro_oficial"],
      "location_id": "106377336067638",
      "first_comment": "Link na bio!",
      "comments_disabled": false
    },
    "created_at": "2026-10-08 14:00:00"
  }
}
GET/posts

Listar posts

Lista os posts do customer, do mais recente para o mais antigo, incluindo as tentativas que falharam. Filtros opcionais: customer_integration, external_reference e status (created, publishing, published ou failed).

ParâmetrosDescrição
customer_integrationO uuid de uma conexão do customer, para ficar só com os posts da conta dela. Uma conexão de outro customer responde 403.
statuscreated, publishing, published ou failed.
external_referenceOs posts com essa referência: as tentativas que falharam e o mesmo conteúdo publicado em contas diferentes.
per_pageDe 1 a 100, padrão 15.
{
  "data": [
    {
      "uuid": "9f1c2b7e-1d3a-4c55-9a8e-2f6b0c1d4e5f",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "external_reference": "post-2026-10-08-001",
      "status": "published",
      "publishing": { "status": "published", "external_id": "18393642439204130", "error": null, "published_at": "2026-10-08 14:00:42" },
      "...": "..."
    },
    {
      "uuid": "1b2e7c90-55aa-4f0e-8d0c-3a9e6f7d2c11",
      "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
      "external_reference": "post-2026-10-08-001",
      "status": "failed",
      "publishing": {
        "status": "failed",
        "external_id": null,
        "error": { "code": "36001", "message": "The aspect ratio is not supported." },
        "published_at": null
      },
      "...": "..."
    }
  ],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "https://connect.reportei.com/api/posts",
    "per_page": 15,
    "to": 2,
    "total": 2,
    "sortBy": null,
    "descending": false
  }
}

Campos da resposta

CampoDescrição
uuidIdentificador do post no Connect. Use nas consultas.
customer_uuidO uuid do customer dono do post.
external_referenceA referência que você enviou.
source_idID da conta de destino na rede, copiado da integração.
statusStatus geral do post: created, publishing, published ou failed. Veja Status da Publicação.
permalinkLink público do post na rede, preenchido depois da publicação. Sempre null em story.
publishingEstado detalhado da publicação: status, external_id (ID do post publicado na rede), error e published_at.
publishing.errornull, ou { code, message } quando a publicação falhou. Veja Erros.
mediaAs mídias na ordem de publicação, com position, type, url, alt_text e as user_tags já normalizadas.
media.*.publishingEstado da publicação de cada mídia (status, external_id e error). Em story, cada mídia é um story com o seu próprio external_id.
instagramAs opções do Instagram enviadas, só com os campos preenchidos. null quando nenhuma foi enviada.
facebookAs opções do Facebook enviadas (link e title), só com os campos preenchidos. null quando nenhuma foi enviada.
threadsAs opções do Threads enviadas, só com os campos preenchidos. null quando nenhuma foi enviada.
linkedinAs opções do LinkedIn enviadas (title). null quando nenhuma foi enviada.
pinterestAs opções do Pinterest enviadas, só com os campos preenchidos. null quando nenhuma foi enviada.
youtubeAs opções do YouTube enviadas, só com os campos preenchidos. null quando nenhuma foi enviada.

A listagem é paginada (per_page de 1 a 100, padrão 15) e traz os posts mais recentes primeiro. Uma external_reference pode ter vários posts: tentativas que falharam e o mesmo conteúdo publicado em contas diferentes.

Status da Publicação

O post tem dois status: post.status é o resumo para a sua interface, e publishing.status é o detalhe do processo.

post.status

StatusDescrição
createdRecebido, aguardando o início da publicação.
publishingPublicação em andamento.
publishedPublicado. publishing.status pode ser published ou partially_published.
failedNada foi publicado. O motivo está em publishing.error.

publishing.status

StatusDescrição
pendingNa fila, aguardando o processamento.
processingMídias enviadas; a rede está processando.
readySó em mídias: a mídia foi processada e está pronta para compor o carrossel ou ser publicada no story.
publishingPublicação sendo enviada à rede.
publishedPublicado. Status final.
partially_publishedSó em story: parte dos stories foi publicada e parte falhou. O que já foi ao ar não é desfeito. Status final.
failedFalhou. Status final.

Acompanhando a publicação

Consulte GET /posts/{uuid} a cada 15 a 30 segundos até um status final (published, partially_published ou failed). Imagens costumam ser publicadas em segundos; vídeos podem levar alguns minutos. Se a rede não terminar de processar a mídia em 1 hora, a publicação falha com publishing_timeout. Falhas temporárias (limite de taxa, instabilidade) são tentadas de novo automaticamente antes de o post falhar.

Receber o resultado por webhook

Em vez de consultar, assine post.published, post.partially_published e post.failed em /webhooks/endpoints (veja Webhooks na referência da API). Cada evento chega com data.object igual ao GET /posts/{uuid}, já no status final.

Primeiro comentário e comentários desativados

first_comment e comments_disabled são aplicados depois que o post vai ao ar. Se uma dessas ações falhar, o post continua published: a publicação não é desfeita por causa delas.

Erros

Os erros aparecem em dois momentos: na requisição, quando o Connect recusa o pedido antes de gravar o post, e na publicação, quando o post foi aceito mas não chegou à rede. Os códigos específicos de cada rede estão na seção da rede.

Erros na requisição

Respondidos na hora. Recusas de negócio trazem message e error_code; erros de validação trazem errors, com as mensagens por campo.

Statuserror_codeDescrição
401Bearer token ausente ou inválido, x-customer-token ausente ou inválido, ou customer com o teste encerrado e sem pagamento.
403O customer_integration informado não é deste customer.
404GET /posts/{uuid} de um post que não existe ou que é de outro customer.
422errorsCorpo inválido ou Idempotency-Key ausente. errors traz as mensagens por campo.
422idempotency_key_reusedA Idempotency-Key já foi usada com outro corpo.
422integration_not_foundO customer não tem integração com esse integration_slug e source_name.
422integration_ambiguousMais de uma integração ativa do customer tem esse source_name. Envie a conta pela conexão, com customer_integration.
422integration_inactiveA integração existe, mas não está ativa (por exemplo, token expirado). Peça ao customer para reconectar a conta.
422carousel_tags_not_supportedCarrossel com user_tags numa conta conectada pelo login do Instagram, que não aceita marcação em carrossel.
409post_in_progressJá existe uma publicação em andamento para a mesma conta e external_reference. Traz post_uuid.
409post_already_publishedA mesma conta e external_reference já foram publicadas. Traz post_uuid.
{
  "message": "A post with this external_reference was already published to this account.",
  "error_code": "post_already_published",
  "post_uuid": "9f1c2b7e-1d3a-4c55-9a8e-2f6b0c1d4e5f"
}
{
  "errors": {
    "media.0.user_tags.0": ["Tags on a video do not take x and y."],
    "instagram.first_comment": ["The first_comment option is not accepted for story."]
  }
}

Erros na publicação

Quando a publicação falha, post.status vira failed e publishing.error traz code e message. Em story, cada mídia tem o seu próprio error, e o post pode terminar como partially_published.

"publishing": {
  "status": "failed",
  "external_id": null,
  "error": {
    "code": "36001",
    "message": "The aspect ratio is not supported."
  },
  "published_at": null
}
Códigos gerais
error.codeDescrição
integration_inactiveA integração deixou de estar ativa antes da publicação.
publishing_timeoutA rede não terminou de processar a mídia em 1 hora.
generator_invalid_requestO serviço de publicação recusou o pedido. Fale com o suporte informando o uuid do post.
unexpected_errorErro inesperado depois de esgotadas as tentativas. Fale com o suporte informando o uuid do post.

Toolkits

Os toolkits conectam os seus clientes a apps de terceiros que não são plataformas de métricas, como Notion, Jira e Google Calendar. Cada toolkit tem tools, as operações que você executa na conta do cliente: buscar páginas no Notion, ler ou criar uma issue no Jira, criar um evento na agenda. A resposta de cada execução é a do próprio app. Os toolkits não têm coleta de métricas nem webhooks: são ações sob demanda.

Como funciona

  1. Escolha o toolkit e a tool no catálogo. O input de cada tool é o JSON Schema dos argumentos, o que você entrega a um agente para ele escolher a tool e preencher a chamada.
  2. Conecte o cliente ao app pela sessão de integração, com toolkit_slug, ou por POST /customer-toolkits, quando você já tem a chave dele. A conexão ganha um uuid.
  3. Execute a tool com POST /tools/{tool_slug}/execute, passando o uuid da conexão em customer_toolkit e os argumentos em arguments.

No modo oauth2, o toolkit conecta pelo seu app OAuth, não por um app do Reportei. Cadastre o client ID e o client secret em POST /merchants/credentials/{toolkit_slug} antes de abrir a sessão. Sem esse cadastro, um toolkit que só conecta por OAuth, como o Google Calendar, recusa a sessão com 422 toolkit_credential_missing. O passo a passo está em Toolkits OAuth com o seu app.

Catálogo

O catálogo é o mesmo para todo merchant e não depende de cliente, então as rotas dele levam só o Bearer. Cada toolkit diz em auth os modos de conexão que aceita. Nos modos api_key e basic, fields traz o rótulo de cada campo e steps diz onde o cliente encontra a credencial no app, o que serve para montar o seu próprio formulário. logo_url traz o logo oficial do app, servido pelo Connect, para você mostrar no seu catálogo: um SVG, ou um PNG quando a marca só publica o ícone em PNG. Vem null quando o app não tem logo, e a sessão mostra a inicial do nome no lugar. Leia a lista em GET /toolkits em vez de fixá-la no seu código: apps novos entram no catálogo sem mudança na API.

modeO que o cliente informaComo conectar
api_keyUma chave de API, em key.Pela sessão, num formulário, ou por POST /customer-toolkits.
basicUsuário e senha, em username e password. No Jira, o e-mail da conta e um token de API.Pela sessão, num formulário, ou por POST /customer-toolkits.
oauth2Nenhuma credencial: o cliente autoriza no consentimento do app.Só pela sessão, com o seu app OAuth cadastrado para o toolkit.

Variável do host

Alguns apps têm um endereço por conta, como o Jira Cloud em acme.atlassian.net. O toolkit declara a variável em base_url_params, como site no Jira, e o cliente informa só o subdomínio: letras minúsculas, números e hífen, até 63 caracteres, começando por letra ou número. O domínio é sempre o do app, então a credencial nunca vai para outro endereço.

GET/toolkits

Listar toolkits

O catálogo inteiro, sem paginação. Filtre por category, como productivity, project_management ou scheduling, e por search, que procura no slug, no nome e na descrição. tools_count diz quantas tools cada toolkit tem.

{
  "data": [
    {
      "slug": "google_calendar",
      "name": "Google Calendar",
      "description": "Calendars and events of a Google account.",
      "category": "scheduling",
      "version": "2026-10-19",
      "docs_url": "https://developers.google.com/workspace/calendar/api/v3/reference",
      "logo_url": "https://connect.reportei.com/img/toolkits/google_calendar.svg",
      "auth": [
        {
          "mode": "oauth2",
          "merchant_credential_required": true,
          "scopes": [
            "openid",
            "email",
            "profile",
            "https://www.googleapis.com/auth/calendar.calendarlist.readonly",
            "https://www.googleapis.com/auth/calendar.events"
          ]
        }
      ],
      "base_url_params": {},
      "tools_count": 3
    },
    {
      "slug": "jira",
      "name": "Jira",
      "description": "Issues and projects of a Jira Cloud site.",
      "category": "project_management",
      "version": "2026-10-19",
      "docs_url": "https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/",
      "logo_url": "https://connect.reportei.com/img/toolkits/jira.svg",
      "auth": [
        {
          "mode": "basic",
          "merchant_credential_required": false,
          "fields": {
            "username": {
              "label": "Atlassian account email",
              "placeholder": "you@company.com"
            },
            "password": {
              "label": "API token",
              "placeholder": "Paste your API token"
            }
          },
          "steps": [
            "Sign in to id.atlassian.com with the account you are connecting and open Security, then API tokens.",
            "Choose Create API token, the option without scopes, and set an expiration of up to one year.",
            "Paste the token below together with the email of the same account."
          ]
        }
      ],
      "base_url_params": {
        "site": {
          "label": "Site name",
          "placeholder": "your-company, from your-company.atlassian.net",
          "format": "subdomain"
        }
      },
      "tools_count": 3
    }
  ]
}
GET/toolkits/{toolkit_slug}

Consultar toolkit

Um toolkit pelo slug, com os modos de conexão, os campos que cada um pede ao cliente e as variáveis do host. Responde 404 toolkit_not_found quando nenhum toolkit habilitado tem esse slug.

{
  "toolkit": {
    "slug": "jira",
    "name": "Jira",
    "description": "Issues and projects of a Jira Cloud site.",
    "category": "project_management",
    "version": "2026-10-19",
    "docs_url": "https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/",
    "logo_url": "https://connect.reportei.com/img/toolkits/jira.svg",
    "auth": [
      {
        "mode": "basic",
        "merchant_credential_required": false,
        "fields": {
          "username": {
            "label": "Atlassian account email",
            "placeholder": "you@company.com"
          },
          "password": {
            "label": "API token",
            "placeholder": "Paste your API token"
          }
        },
        "steps": [
          "Sign in to id.atlassian.com with the account you are connecting and open Security, then API tokens.",
          "Choose Create API token, the option without scopes, and set an expiration of up to one year.",
          "Paste the token below together with the email of the same account."
        ]
      }
    ],
    "base_url_params": {
      "site": {
        "label": "Site name",
        "placeholder": "your-company, from your-company.atlassian.net",
        "format": "subdomain"
      }
    },
    "tools_count": 3
  }
}
GET/toolkits/{toolkit_slug}/tools

Listar tools

As tools de um toolkit, sem paginação. method diz se a tool lê (GET) ou escreve no app, e input é o JSON Schema dos arguments que a execução recebe.

{
  "data": [
    {
      "slug": "jira_get_issue",
      "toolkit_slug": "jira",
      "name": "Get issue",
      "description": "Returns one issue by id or key.",
      "method": "GET",
      "input": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "issueIdOrKey"
        ],
        "properties": {
          "issueIdOrKey": {
            "type": "string",
            "description": "Issue id or key, for example KEY-123."
          },
          "fields": {
            "type": "string",
            "description": "Comma-separated field names to return. Omit it to return all navigable fields."
          }
        }
      }
    }
  ]
}
GET/tools/{tool_slug}

Consultar tool

Uma tool pelo slug, que é global e já traz o prefixo do toolkit, como jira_get_issue. Vem com o toolkit a que pertence e o input.

{
  "tool": {
    "slug": "jira_get_issue",
    "toolkit_slug": "jira",
    "name": "Get issue",
    "description": "Returns one issue by id or key.",
    "method": "GET",
    "input": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "issueIdOrKey"
      ],
      "properties": {
        "issueIdOrKey": {
          "type": "string",
          "description": "Issue id or key, for example KEY-123."
        },
        "fields": {
          "type": "string",
          "description": "Comma-separated field names to return. Omit it to return all navigable fields."
        }
      }
    }
  }
}

Conectar o cliente

Cada conexão é uma conta do app ligada a um cliente. Ela fica em GET /customer-toolkits, separada das conexões de plataforma, e o uuid dela é o que a execução recebe em customer_toolkit. As rotas de conexão e de execução levam o Bearer e o x-customer-token do cliente. Como em Posts e Ads, um cliente com o teste encerrado e sem pagamento recebe 401 customer_trial_ended nelas e na sessão, até ser marcado como pagante.

Pela sessão de integração

POST/customer-integrations/session

Abra a sessão com toolkit_slug, que não aceita source_slug junto. O cliente vê o formulário de credencial do toolkit, ou vai para o consentimento do app quando o toolkit conecta por OAuth, e informa na sessão o valor da variável do host, se houver. Num toolkit que aceita chave e OAuth, como o Notion, a sessão usa o OAuth quando o seu app está cadastrado para ele, e o formulário de chave quando não está.

{
  "toolkit_slug": "jira",
  "redirect_url": "https://sua-aplicacao.com/integracoes"
}

Ao terminar, o cliente volta para a sua redirect_url com o uuid da conexão em customer_toolkit. Com close_on_finish, a aba mostra a conta conectada e fecha; a conexão aparece em GET /customer-toolkits.

https://sua-aplicacao.com/integracoes?customer_toolkit=7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e

Status da conexão

Uma conexão é active ou expired. Ela vira expired quando o seu app OAuth que a autorizou é trocado ou removido, ou quando o app recusa renovar o token. Nos modos api_key e basic isso não acontece. Reconectar a mesma conta volta a conexão para active.

POST/customer-toolkits

Conectar com chave

Para quando você já tem a credencial do cliente: a mesma conexão que a sessão faz, sem o formulário. O Connect envia a credencial ao app uma vez para identificar a conta e só então a guarda. Só nos modos api_key e basic: um toolkit que conecta só por OAuth responde 422 toolkit_requires_session. Conectar de novo a mesma conta troca a credencial guardada e volta a conexão para active.

Parâmetros

CampoDescrição
toolkit_slugObrigatório. Um toolkit habilitado com modo api_key ou basic.
keyA chave de API do cliente, no modo api_key.
usernameA primeira parte da credencial no modo basic, como o e-mail da conta no Jira.
passwordA segunda parte da credencial no modo basic, como o token de API no Jira.
base_url_paramsO valor de cada variável do host do toolkit, por nome, como site no Jira. Só o subdomínio: acme, de acme.atlassian.net.
{
  "toolkit_slug": "jira",
  "base_url_params": {
    "site": "acme"
  },
  "username": "ana@acme.com",
  "password": "YOUR_API_TOKEN"
}
GET/customer-toolkits

Listar conexões

As conexões de toolkit do cliente, sem paginação. Nenhum segredo sai daqui.

{
  "data": [
    {
      "uuid": "7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e",
      "toolkit": {
        "slug": "jira",
        "name": "Jira"
      },
      "source_id": "acme:5b10ac8d82e05b22cc7d4ef5",
      "source_name": "Ana Lima",
      "status": "active",
      "base_url_params": {
        "site": "acme"
      },
      "expires_at": null,
      "created_at": "2026-10-19T12:00:00.000000Z",
      "updated_at": "2026-10-19T12:00:00.000000Z"
    }
  ]
}
GET/customer-toolkits/{uuid}

Consultar conexão

Uma conexão pelo uuid, que vem de GET /customer-toolkits ou da redirect_url da sessão. A conexão de outro cliente responde 404 customer_toolkit_not_found.

{
  "customer_toolkit": {
    "uuid": "7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e",
    "toolkit": {
      "slug": "jira",
      "name": "Jira"
    },
    "source_id": "acme:5b10ac8d82e05b22cc7d4ef5",
    "source_name": "Ana Lima",
    "status": "active",
    "base_url_params": {
      "site": "acme"
    },
    "expires_at": null,
    "created_at": "2026-10-19T12:00:00.000000Z",
    "updated_at": "2026-10-19T12:00:00.000000Z"
  }
}
DELETE/customer-toolkits/{uuid}

Remover conexão

Apaga a conexão e a credencial guardada com ela. O app não é avisado: a chave ou o consentimento continuam valendo lá, e o cliente os revoga no próprio app se quiser.

{
  "success": true,
  "message": "Record successfully removed"
}

Toolkits OAuth com o seu app

No modo oauth2, o cliente autoriza pelo consentimento do app, e a conexão usa o app OAuth que você cria no app de terceiro, como um client OAuth no Google Cloud para o Google Calendar. Não há app do Reportei para toolkits: sem o seu, o modo oauth2 não conecta. O cadastro usa as mesmas rotas de Use sua aplicação, com o slug do toolkit em integration_slug.

1

Crie o app OAuth no painel de desenvolvedor do app de terceiro, cuja documentação está em docs_url, e registre nele a callback_url do Connect como URL de redirecionamento.

2

Cadastre o client ID e o client secret em POST /merchants/credentials/{toolkit_slug}. Sem scopes, o consentimento pede os scopes do toolkit, em auth[].scopes; com scopes, a sua lista substitui a do toolkit por inteiro.

3

Abra a sessão com toolkit_slug. A partir daí, a sessão desse toolkit conecta pelo seu app.

A callback_url do toolkit

Troque {toolkit_slug} pelo slug do toolkit. O endereço também volta em callback_url na resposta do cadastro. Se preferir receber o callback no seu domínio, informe redirect_url no cadastro e redirecione de lá para este endereço, com a query intacta.

https://connect.reportei.com/api/oauth/{toolkit_slug}/callback

O cadastro do app

POST/merchants/credentials/{integration_slug | toolkit_slug}

O mesmo POST de Use sua aplicação, com o slug do toolkit no caminho. A resposta traz a callback_url e active_connections, que conta as conexões ativas feitas com o seu app.

curl -s -X POST https://connect.reportei.com/api/merchants/credentials/google_calendar \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "1234567890-abc123def456.apps.googleusercontent.com",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'

Importante

Um toolkit que só conecta por OAuth, como o Google Calendar, recusa a sessão com 422 toolkit_credential_missing enquanto o seu app não estiver cadastrado para ele.

PUT com outro client_id, e DELETE, marcam como expired as conexões ativas feitas com o app anterior. O total vem em invalidated_connections, e cada cliente reconecta pela sessão. Um client_secret novo do mesmo app não invalida nada.

Renovação do token

Quando o app informa a validade do token, em expires_at, e permite renová-lo, o Connect o renova antes da execução que cair a até um minuto do vencimento. Se o app recusa a renovação, a conexão vira expired e a execução responde 409 customer_toolkit_expired; se a renovação falha por outro motivo, a execução responde 502 ou 504, sem execution_id, e a conexão segue active. Execuções simultâneas na mesma conexão esperam a renovação em curso e usam o token novo; se a espera passa de 30 segundos, a execução responde 409 customer_toolkit_refreshing: repita em seguida.

Executar uma tool

A execução chama o app na conta do cliente, com a credencial da conexão. arguments vai ao app como veio: monte-o pelo input da tool, porque o que o app aceita é decisão dele. O Connect só recusa, antes de chamar o app, um argumento que não serve para montar a URL.

Sem retry e sem idempotência

O Connect chama o app uma vez e não repete. Um POST repetido é um POST a mais no app, e um 504 não garante que a chamada não chegou: não repita às cegas uma tool que escreve. O method da tool diz se ela lê (GET) ou escreve.

O que a execução responde

Decida pelo corpo, não só pelo status: error_code é uma recusa do Connect, e api_error é um erro do app.

StatusDescrição
2xxO status e o corpo do app, no Content-Type dele, sem transformação.
3xx, 4xx, 5xxUm erro do app, com o status dele, no envelope { error, api_error }: o corpo original em api_error.body e, em api_error.headers, o retry-after, os x-ratelimit-*, o x-request-id e o location de um redirect, quando o app os mandou.
404, 409, 422Uma recusa do Connect, antes de chamar o app, com message e error_code. Os códigos estão em Erros desta superfície.
502, 504O app não respondeu a tempo, ou a chamada falhou sem uma resposta dele, com message e error_code. Traz execution_id quando a chamada chegou a ser feita.

A execução fica registrada quando chega à etapa de chamar o app. Se o app responde, o uuid do registro vem no header X-Toolkit-Execution-Id; no 422 tool_arguments_invalid e nos 502 e 504 dessa etapa, ele vem em execution_id, no corpo. As recusas anteriores e as falhas na renovação do token não deixam registro. Toda resposta que vem do app, de sucesso ou de erro, traz também o header X-Content-Type-Options: nosniff, porque o corpo é do app e o navegador não deve adivinhar outro tipo.

POST/tools/{tool_slug}/execute

Executar tool

Chama a tool na conexão do cliente e devolve a resposta do app como veio. O curl do exemplo usa -i para mostrar o header X-Toolkit-Execution-Id.

Parâmetros

CampoDescrição
customer_toolkitObrigatório. O uuid da conexão, que precisa ser do toolkit da tool e estar active.
argumentsOs argumentos da tool, num objeto de até 64 KB no formato do input dela. Vai ao app como veio. Pode faltar quando a tool não tem argumento obrigatório.

Erro do app

Um 404 do Jira, como chega: o status é o do app, e o corpo dele vem em api_error.body.

{
  "error": "Request failed with status code 404",
  "api_error": {
    "status_code": 404,
    "headers": {
      "x-request-id": "6f1c0e2a"
    },
    "body": {
      "errorMessages": [
        "Issue does not exist or you do not have permission to see it."
      ],
      "errors": {}
    }
  }
}
{
  "customer_toolkit": "7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e",
  "arguments": {
    "issueIdOrKey": "KEY-123",
    "fields": "summary,status"
  }
}
GET/tools/executions/{uuid}

Consultar execução

O registro de uma execução do cliente, pelo X-Toolkit-Execution-Id: o que foi pedido ao app, a URL alcançada sem a query string, o status e a resposta do app e quanto demorou. A resposta não traz os headers enviados. A execução de outro cliente responde 404 toolkit_execution_not_found.

{
  "execution": {
    "uuid": "0d3c8b4a-2e1f-4c6d-9a7b-5e4f3a2b1c0d",
    "tool_slug": "jira_get_issue",
    "toolkit_slug": "jira",
    "customer_toolkit": "7c2a1d3e-5f6b-4a8c-9d0e-1f2a3b4c5d6e",
    "customer_uuid": "0b6e1c2a-3f4d-4e5f-8a9b-1c2d3e4f5a6b",
    "method": "GET",
    "url": "https://acme.atlassian.net/rest/api/3/issue/KEY-123",
    "payload": {
      "issueIdOrKey": "KEY-123",
      "fields": "summary,status"
    },
    "http_status": 200,
    "response": {
      "id": "10001",
      "key": "KEY-123",
      "fields": {
        "summary": "Fix the login",
        "status": {
          "name": "In Progress"
        }
      }
    },
    "error_message": null,
    "duration_ms": 412,
    "created_at": "2026-10-19T12:00:00.000000Z"
  }
}

Execuções de todos os clientes

Para acompanhar as execuções de todos os seus clientes, só com o Bearer, use a rota de panorama na seção Merchants:

GET/merchants/tools/executions

Limitações

  • O texto que vem da spec do toolkit, como as descrições, os rótulos e os passos do formulário, é só em inglês. As frases do próprio Connect na sessão, como botões e erros, seguem o locale.
  • Um erro do app numa execução não muda a conexão: um 401 do app volta como veio, e a conexão segue active. Reconectar é decisão sua.
  • A sessão de toolkit não dispara os webhooks integration_session.* nem customer_integration.*. Acompanhe pelo customer_toolkit da redirect_url ou por GET /customer-toolkits.
  • O Connect não segue redirect: um 3xx volta como o app respondeu, com o location, e a credencial nunca vai para outro endereço.
  • A credencial da conexão nunca aparece numa resposta: se o app a ecoar, ela volta como [redacted], inclusive codificada (o par usuário:senha do modo basic em base64, url-encoded ou com as barras escapadas em JSON). No modo basic, o usuário também é tratado como segredo, então um app que devolve o e-mail da própria conta, como o Jira, mostra [redacted] no lugar dele.
  • O registro de GET /tools/executions/{uuid} guarda payload e response decodificados: um {} aninhado aparece como [], e uma resposta acima de 64 KB, ou que não é JSON, vira { truncated, body }, com o texto cortado. O corpo que a execução devolve é o original.

Erros desta superfície

Além dos erros comuns da API, estas são as recusas do Connect nas rotas de toolkits, com message e error_code. Falha de validação do corpo ou da query volta 422 com errors.

Statuserror_codeDescrição
404toolkit_not_foundNenhum toolkit habilitado tem esse slug.
404tool_not_foundNenhum toolkit habilitado tem uma tool com esse slug.
404customer_toolkit_not_foundA conexão não existe ou não é deste cliente. A API não distingue os dois casos.
404toolkit_execution_not_foundA execução não existe ou não é deste cliente.
409customer_toolkit_expiredA conexão expirou: o seu app OAuth que a autorizou foi trocado ou removido, ou o app recusou renovar o token. Reconecte pela sessão.
409customer_toolkit_refreshingOutra chamada está renovando o token desta conexão, e a espera passou de 30 segundos. Repita em seguida.
422toolkit_credential_missingSessão de um toolkit que só conecta por OAuth, sem o seu app cadastrado para ele.
422toolkit_requires_sessionPOST /customer-toolkits com um toolkit que só conecta por OAuth. Use a sessão.
422toolkit_credential_invalidO app recusou a credencial informada, ou não identificou a conta.
422toolkit_host_param_invalidO valor de uma variável do host não é um subdomínio válido.
422tool_toolkit_mismatchA tool não é do toolkit desta conexão.
422tool_arguments_invalidUm argumento que monta a URL está ausente ou vazio, ou é "." ou "..", ou um argumento de query não é um valor simples nem uma lista deles. Traz execution_id.
502toolkit_integration_errorA chamada falhou sem uma resposta do app, ou a renovação do token falhou sem uma recusa dele. Na conexão sem tela, também quando o app responde com erro ao identificar a conta. Na execução, traz execution_id quando a chamada chegou a ser feita.
504toolkit_timeoutO app não respondeu em 25 segundos, ou a chamada não terminou em 30. Ela pode ter chegado ao app: não repita às cegas uma tool que escreve. Traz execution_id quando a chamada chegou a ser feita.

Os erros do próprio app não estão nesta tabela: eles chegam com o status do app e o envelope { error, api_error }, sem error_code.

LLM

Modelos da OpenAI e da Anthropic por uma API só, no formato da API da OpenAI, com o mesmo token do Connect. O SDK oficial da OpenAI funciona sem adaptação: você troca o endereço base e o nome do modelo, e passa a chamar GPT e Claude pelo mesmo código, com stream, tools, imagens e fallback entre modelos.

Como funciona

  1. Cadastre a sua chave de API da OpenAI ou da Anthropic em POST /merchants/credentials/openai ou /merchants/credentials/anthropic.
  2. Aponte o SDK da OpenAI para https://connect.reportei.com/api/llm e use o token do Connect como apiKey.
  3. Chame client.chat.completions.create() com o id de um modelo de GET /llm/models, como openai/gpt-5.4-mini ou anthropic/claude-sonnet-5-5.

Sempre com a sua chave

O Connect nunca chama a OpenAI ou a Anthropic com uma chave do Reportei. Cada chamada usa a chave que você cadastrou, e o consumo é cobrado na sua conta do provedor. Sem a chave do provedor de um modelo, a chamada é recusada com 400 llm_credential_missing. O Connect registra os tokens e o custo estimado de cada chamada, mas não impõe teto de gasto: use os limites da sua conta no provedor.

Com o SDK da OpenAI

Use este endereço como baseURL (base_url no Python) e o token de acesso do Connect como apiKey. client.models.list() e client.chat.completions.create() funcionam como na OpenAI. Não é preciso enviar x-customer-token: o uso é da conta do merchant.

https://connect.reportei.com/api/llm
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.CONNECT_API_TOKEN,
  baseURL: "https://connect.reportei.com/api/llm",
});

const stream = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5-5",
  max_tokens: 1024,
  stream: true,
  messages: [{ role: "user", content: "Resuma a semana da conta." }],
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["CONNECT_API_TOKEN"],
    base_url="https://connect.reportei.com/api/llm",
)

response = client.chat.completions.create(
    model="openai/gpt-5.4-mini",
    max_tokens=512,
    messages=[{"role": "user", "content": "Resuma a semana da conta."}],
)
print(response.choices[0].message.content)

Cadastrar a sua chave

As mesmas rotas de Use sua aplicação, com o provedor no lugar do slug: openai ou anthropic. O corpo é só api_key. A chave fica guardada cifrada e nunca volta numa resposta: o Connect mostra só os 4 últimos caracteres, em api_key_hint. Para trocar a chave, use o PUT; ao removê-la, as chamadas para os modelos do provedor passam a responder 400 llm_credential_missing.

POST/merchants/credentials/{openai | anthropic}

Cadastrar a chave do provedor

Guarda a sua chave de API da OpenAI (openai) ou da Anthropic (anthropic). Uma por provedor: se já houver uma, use o PUT.

{
  "api_key": "sk-proj-..."
}
GET/merchants/credentials/{openai | anthropic}

Ver a chave cadastrada

Mostra se há chave para o provedor e os 4 últimos caracteres dela. A chave em si nunca volta.

{
  "credential": {
    "integration": "anthropic",
    "api_key_hint": "...9xQz",
    "created_at": "2026-11-03T12:00:00.000000Z",
    "updated_at": "2026-11-03T12:00:00.000000Z"
  }
}
PUT/merchants/credentials/{openai | anthropic}

Trocar a chave

Substitui a chave do provedor. As próximas chamadas já usam a nova.

{
  "api_key": "sk-proj-..."
}
DELETE/merchants/credentials/{openai | anthropic}

Remover a chave

Apaga a chave do provedor. As chamadas para os modelos dele passam a responder 400 llm_credential_missing.

{
  "message": "Credential removed"
}

Modelos

Lista os modelos que você pode chamar, no formato de GET /v1/models da OpenAI, só dos provedores em que você cadastrou a chave. Cada modelo traz os limites (context_window e max_output_tokens), o que ele atende (supports_tools e supports_vision) e o preço por 1 milhão de tokens. Em long_context vem a faixa de prompt longo, quando o provedor cobra mais acima de um tamanho de entrada: a chamada inteira passa a custar o preço da faixa.

GET/llm/models

Listar modelos

Os modelos que você pode chamar, só dos provedores com chave cadastrada, com limites, recursos e preço.

{
  "data": [
    {
      "id": "anthropic/claude-haiku-5-5",
      "object": "model",
      "created": 1792670400,
      "owned_by": "anthropic",
      "context_window": 1000000,
      "max_output_tokens": 128000,
      "supports_tools": true,
      "supports_vision": true,
      "pricing": {
        "input_per_mtok": 0.1,
        "output_per_mtok": 0.5,
        "long_context": {
          "above_input_tokens": 100000,
          "input_per_mtok": 0.5,
          "output_per_mtok": 2.5
        }
      }
    },
    {
      "id": "openai/gpt-5.4-mini",
      "object": "model",
      "created": 1792670400,
      "owned_by": "openai",
      "context_window": 400000,
      "max_output_tokens": 128000,
      "supports_tools": true,
      "supports_vision": true,
      "pricing": {
        "input_per_mtok": 0.75,
        "output_per_mtok": 4.5,
        "long_context": null
      }
    }
  ],
  "object": "list"
}

Chat completions

O POST /v1/chat/completions da OpenAI, para os modelos da OpenAI e da Anthropic: mesmo corpo, mesma resposta e mesmo stream. Na resposta, model diz qual modelo respondeu.

POST/llm/chat/completions

Chat completion

Gera a resposta do modelo, em JSON ou em stream. O exemplo do corpo usa fallback para a Anthropic; o curl usa stream (-N para ver os pedaços chegando).

Parâmetros

CampoDescrição
modelObrigatório. O id de um modelo de GET /llm/models, no formato provedor/modelo.
modelsOpcional. Até 3 modelos de fallback, tentados em ordem quando model falha antes do primeiro token. Pode misturar provedores, desde que você tenha a chave de cada um.
messagesObrigatório. As mensagens no formato da OpenAI: system, developer, user, assistant e tool, com texto, partes image_url e tool_calls.
max_tokensObrigatório em toda chamada (ou max_completion_tokens): é o limite do custo dela. Não pode passar do teto da sua conta (4096 por padrão) nem do max_output_tokens de nenhum modelo da chamada.
streamtrue para receber a resposta em text/event-stream, pedaço a pedaço.
stream_options.include_usagetrue para receber, no fim do stream, um pedaço com usage (tokens de entrada e de saída).
toolsFunções que o modelo pode chamar, como na OpenAI, com tool_choice. Só nos modelos com supports_tools.
response_formatResposta em JSON (json_object ou json_schema). Só nos modelos da OpenAI.
reasoning_effortO esforço de raciocínio, como na OpenAI. Só nos modelos da OpenAI.

O stream

Com stream: true, a resposta é text/event-stream: uma linha data: com um pedaço em JSON, uma linha em branco entre eles, e data: [DONE] no fim, como na OpenAI.

data: {"id":"chatcmpl-9f2c4e1a","object":"chat.completion.chunk","created":1792670400,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"role":"assistant","content":"Na semana"},"finish_reason":null}]}

data: {"id":"chatcmpl-9f2c4e1a","object":"chat.completion.chunk","created":1792670400,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"content":", o alcance subiu 12%."},"finish_reason":"stop"}]}

data: [DONE]
{
  "model": "openai/gpt-5.4-mini",
  "models": ["anthropic/claude-haiku-5-5"],
  "max_tokens": 512,
  "messages": [
    { "role": "system", "content": "Você resume dados de marketing em português." },
    { "role": "user", "content": "Resuma a semana da conta." }
  ]
}

Regras e limites

  • max_tokens é obrigatório e tem teto: o menor entre o limite da sua conta e o max_output_tokens de cada modelo da chamada, fallbacks incluídos.
  • Cada conta tem, por padrão, até 4 chamadas abertas ao mesmo tempo. Acima disso, a resposta é 429 llm_concurrency_limit: espere uma terminar.
  • tools, tool_choice e partes image_url só passam se todos os modelos da chamada atendem (supports_tools e supports_vision em GET /llm/models). Senão, 400 llm_unsupported_field.
  • Não são aceitos: n diferente de 1, audio, modalities, prediction, logprobs, top_logprobs e logit_bias. Vazio, null, false e 0 contam como ausentes. O corpo vai até 1 MB.
  • O fallback só acontece antes do primeiro token (erro do provedor, limite de requisições ou demora para começar). Depois que a resposta começou, um erro vai para você, e nunca há nova tentativa no mesmo modelo.
  • Sem stream, a resposta pode começar com espaços em branco enquanto o modelo gera, para a conexão não cair em respostas longas. Espaço antes do JSON continua sendo JSON válido.
  • Um erro depois que a resposta começou não muda o status 200: com stream, chega como data: {"error": {...}} e o stream termina sem data: [DONE]; sem stream, o corpo é o JSON de erro. O SDK da OpenAI levanta um erro nos dois casos.
  • Na Anthropic, o Connect traduz system e developer, texto, imagens, tools e tool_calls, tool_choice, stop, max_tokens, stream e usage. temperature, top_p e top_k são ignorados, e response_format só aceita text.
  • Na OpenAI, só vão ao provedor os campos que o gateway atende: messages, temperature, top_p, frequency_penalty, presence_penalty, stop, seed, tools, tool_choice, parallel_tool_calls, response_format, reasoning_effort, user e os limites de tokens. Outros, como store, service_tier e metadata, são descartados.

Erros

As recusas destas rotas vêm no formato de erro da OpenAI, que o SDK da OpenAI lê: {"error": {"message", "type", "code"}}. Decida pelo error.code. As recusas de autenticação (401), de pagamento da conta (403 merchant_access_denied) e de limite de requisições (429) são as mesmas do resto da API.

{
  "error": {
    "message": "This account has no anthropic API key. The gateway only calls a provider with the account's own key: register it with POST /merchants/credentials/anthropic.",
    "type": "invalid_request_error",
    "code": "llm_credential_missing"
  }
}
Statuserror.codeDescrição
400llm_credential_missingVocê não cadastrou a chave do provedor de um modelo da chamada (o model ou um de models).
400llm_provider_credential_invalidO provedor recusou a chave cadastrada. Troque com PUT /merchants/credentials/{provedor}.
400llm_model_not_allowedmodel ou um de models não está em GET /llm/models, ou models tem mais de 3 itens.
400llm_max_tokens_requiredFaltou max_tokens (ou max_completion_tokens), ou ele não é um inteiro positivo.
400llm_max_tokens_exceededmax_tokens passou do teto da conta ou do max_output_tokens de um modelo da chamada.
400llm_unsupported_fieldUm campo que o gateway não aceita, ou tools e imagens num modelo que não atende.
400llm_invalid_requestO corpo não é um objeto JSON, ou messages não tem a forma da OpenAI.
403llm_not_enabledO gateway foi desligado para a sua conta. Fale com o suporte.
413llm_request_too_largeO corpo passou de 1 MB.
429llm_concurrency_limitA conta já tem o máximo de chamadas abertas. Espere uma terminar.
4XXllm_provider_errorO provedor recusou o pedido. Vem com o status e a mensagem do provedor.
500llm_internal_errorErro do Connect. Tente de novo.
502llm_upstream_errorO provedor falhou e o fallback acabou. Tente de novo mais tarde.
503llm_offload_not_configuredO gateway está temporariamente indisponível. Tente de novo mais tarde.
504llm_timeoutO modelo demorou demais para responder.

Webhooks

Cadastre endpoints para receber os eventos das integrações dos seus clientes (token expirado, integração removida, sessão concluída) assim que eles acontecem, sem precisar consultar a API. Toda entrega é um POST assinado, com o mesmo envelope.

Eventos disponíveis

Ao cadastrar um endpoint, você escolhe no campo events quais eventos ele recebe. A lista exata que a sua conta pode assinar vem em available_events, no GET /webhooks/endpoints:

EventoDescrição
customer_integration.status_updatedDisparado quando o status de uma integração muda (ex: token expirou e a integração precisa ser reconectada).
customer_integration.deletedDisparado quando uma integração de cliente é removida.
integration_session.oauth_startedDisparado quando um cliente inicia o fluxo de autenticação OAuth de uma integração.
integration_session.oauth_savedDisparado quando as credenciais OAuth de uma integração são salvas com sucesso.
integration_session.completedDisparado quando uma sessão de integração é concluída.

Formato do evento

Todo evento chega no mesmo envelope. data.object é o recurso como o GET dele devolve, e em customer_integration.status_updated data.previous_attributes traz o status anterior. Use o id do evento para deduplicar: a entrega é pelo menos uma vez e a ordem não é garantida.

{
  "id": "evt_01k7a9x1c3e5g7j9k1m3p5r7t9",
  "object": "event",
  "type": "customer_integration.status_updated",
  "api_version": "v1",
  "created": 1791300000,
  "livemode": true,
  "data": {
    "object": {
      "uuid": "5d1c3b9a-2e7f-4c1d-8a6b-0f3e9d2c7b14",
      "source_id": "17841400000000000",
      "source_name": "conta_exemplo",
      "status": "expired",
      "integration": { "name": "Instagram Business", "slug": "instagram_business" }
    },
    "previous_attributes": { "status": "active" }
  }
}

Verificando a autenticidade das notificações

Toda entrega leva o header Connect-Signature no formato t={timestamp},v1={assinatura}. A assinatura é um HMAC-SHA256 de {timestamp}.{corpo} com o secret do endpoint como chave, calculado sobre os bytes do corpo exatamente como chegaram.

Recalcule o HMAC do seu lado, compare em tempo constante com cada v1 (nas 24 horas seguintes a uma troca de secret vêm dois, um de cada secret) e recuse um timestamp com mais de 5 minutos de diferença do seu relógio.

import crypto from "node:crypto";

// rawBody: the request body exactly as received, before any JSON parsing
function isFromConnect(rawBody, signatureHeader, secret) {
  const timestamp = signatureHeader.match(/t=(\d+)/)?.[1];
  const signatures = [...signatureHeader.matchAll(/v1=([a-f0-9]{64})/g)].map((m) => m[1]);

  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return false;
  }

  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");

  return signatures.some((signature) =>
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  );
}

Entrega e novas tentativas

Responda com qualquer 2xx em até 10 segundos. Outra resposta, um redirect ou nenhuma resposta contam como falha, e o evento é reenviado ao longo de cerca de 3 dias (5 min, 30 min, 2 h, 6 h, 12 h, 24 h e 24 h). Um endpoint que passa 72 horas falhando sem parar é desativado (disabled_reason failures), e responder 410 o desativa na hora (gone). Uma falha que chega mais de 25 horas depois da anterior começa a contagem de novo. Reative com PUT e active true.

GET/webhooks/endpoints

Listar endpoints

Retorna os endpoints da conta com o estado de saúde de cada um, os eventos que a conta pode assinar (available_events) e o limite de endpoints.

{
  "data": [
    {
      "id": "9b2f7c1e-4a3d-4f8e-9c21-6d0a5b8e1f34",
      "url": "https://sua-aplicacao.com/webhooks/connect",
      "description": "Produção",
      "events": ["customer_integration.status_updated", "integration_session.completed"],
      "active": true,
      "disabled_at": null,
      "disabled_reason": null,
      "consecutive_failures": 0,
      "first_failure_at": null,
      "last_failure_at": null,
      "last_success_at": "2026-10-13 12:00:00",
      "created_at": "2026-10-01 09:30:00",
      "updated_at": "2026-10-13 12:00:00"
    }
  ],
  "available_events": [
    "customer_integration.status_updated",
    "customer_integration.deleted",
    "integration_session.oauth_started",
    "integration_session.oauth_saved",
    "integration_session.completed"
  ],
  "limit": 5
}
POST/webhooks/endpoints

Cadastrar endpoint

Cadastra um endereço para receber os eventos escolhidos. A resposta traz o secret, e esta é a única vez que ele aparece.

Parâmetros

NomeDescrição
urlEndereço https que recebe os eventos (obrigatório). Não pode apontar para rede privada nem repetir uma URL já cadastrada na conta.
eventsArray com pelo menos um dos eventos acima (obrigatório).
descriptionUm nome para você reconhecer o endpoint (opcional, até 255 caracteres).
activeSó na alteração: false para as entregas (disabled_reason manual); true reativa e zera a contagem de falhas, sem reenviar o que se perdeu.

Cada conta pode ter até 5 endpoints. O secret que assina as entregas só aparece na resposta da criação e na troca de secret: guarde-o na hora.

{
  "url": "https://sua-aplicacao.com/webhooks/connect",
  "events": ["customer_integration.status_updated", "integration_session.completed"],
  "description": "Produção"
}
GET/webhooks/endpoints/{uuid}

Consultar endpoint

Retorna o endpoint e a saúde dele: consecutive_failures, first_failure_at, last_success_at e, se ele foi desativado, disabled_at e disabled_reason. Nunca devolve o secret.

{
  "webhook_endpoint": {
    "id": "9b2f7c1e-4a3d-4f8e-9c21-6d0a5b8e1f34",
    "url": "https://sua-aplicacao.com/webhooks/connect",
    "description": "Produção",
    "events": ["customer_integration.status_updated", "integration_session.completed"],
    "active": true,
    "disabled_at": null,
    "disabled_reason": null,
    "consecutive_failures": 0,
    "first_failure_at": null,
    "last_failure_at": null,
    "last_success_at": "2026-10-13 12:00:00",
    "created_at": "2026-10-01 09:30:00",
    "updated_at": "2026-10-13 12:00:00"
  }
}
PUT/webhooks/endpoints/{uuid}

Alterar endpoint

Altera url, events, description ou active. Envie só os campos que mudam.

{
  "active": true,
  "events": ["customer_integration.status_updated"]
}
DELETE/webhooks/endpoints/{uuid}

Remover endpoint

Para de entregar na hora, inclusive as tentativas já agendadas, e remove o endpoint.

{
  "success": true
}
POST/webhooks/endpoints/{uuid}/rotate-secret

Trocar o secret

Gera um secret novo. O anterior continua valendo por 24 horas e, nesse período, cada entrega leva as duas assinaturas.

{
  "webhook_endpoint": {
    "id": "9b2f7c1e-4a3d-4f8e-9c21-6d0a5b8e1f34",
    "secret": "whsec_Hk2m9QpX4tVb7Lw1Zr8yNc5sJ3dF6gA0eUoTiRqWxYz"
  }
}
POST/webhooks/endpoints/{uuid}/test

Enviar evento de teste

Reenvia agora o evento mais recente da conta do primeiro tipo que o endpoint assina, com id novo e livemode false, e mostra o que o seu endereço respondeu. Se ele responder 2xx, a sequência de falhas do endpoint é zerada; um teste que falha não conta. Limite de 10 testes por minuto.

{
  "webhook_delivery": {
    "id": "dlv_01k7a9x2m4p6q8r0s2t4v6w8y0",
    "event_id": null,
    "event_type": null,
    "attempt": 1,
    "status": "succeeded",
    "response_status": 200,
    "response_excerpt": "ok",
    "error": null,
    "duration_ms": 184
  }
}
GET/webhooks/endpoints/{uuid}/deliveries

Listar entregas

As 20 tentativas mais recentes do endpoint, com status, resposta do seu endereço e erro. O histórico é guardado por 30 dias.

{
  "data": [
    {
      "id": "dlv_01k7a9x2m4p6q8r0s2t4v6w8y0",
      "event_id": "evt_01k7a9x1c3e5g7j9k1m3p5r7t9",
      "event_type": "customer_integration.status_updated",
      "attempt": 2,
      "status": "failed",
      "response_status": 500,
      "response_excerpt": "Internal Server Error",
      "error": null,
      "duration_ms": 312,
      "next_attempt_at": "2026-10-13 12:35:00",
      "created_at": "2026-10-13 12:05:00"
    }
  ]
}
POST/webhooks/endpoints/{uuid}/deliveries/{delivery}/redeliver

Reenviar entrega

Recomeça o ciclo completo de tentativas de um evento. Só a tentativa mais recente do evento pode ser reenviada, e só se ela esgotou (status exhausted); qualquer outra responde 422.

{
  "webhook_delivery": {
    "id": "dlv_01k7aa0b2c4d6e8f0g2h4j6k8m",
    "event_id": "evt_01k7a9x1c3e5g7j9k1m3p5r7t9",
    "attempt": 9,
    "status": "pending"
  }
}

Formato anterior: /webhook-subscriptions

As rotas abaixo são o formato anterior, mantido para quem já usa: corpo sem envelope, nomes antigos de eventos (oauth_started, oauth_saved, session_completed) e uma assinatura que não cobre o corpo. Integrações novas devem usar /webhooks/endpoints.

Toda requisição enviada para a sua url cadastrada inclui os headers x-api-token e x-api-signature, permitindo validar que a notificação realmente veio do Reportei Connect antes de processá-la. O header x-api-signature tem o formato t={timestamp},s={assinatura}, onde a assinatura é um HMAC-SHA256 de {timestamp}{client_id} usando o seu client_secret como chave. Recalcule esse hash do seu lado e compare com o valor recebido para confirmar a autenticidade.

GET/webhook-subscriptions

Listar Webhooks

Retorna uma lista paginada dos webhooks cadastrados para o seu merchant.

{
  "data": [
    {
      "id": 12,
      "url": "https://sua-aplicacao.com/webhooks/reportei",
      "events": ["customer_integration.status_updated", "customer_integration.deleted"],
      "is_active": true,
      "created_at": "2026-08-05T18:23:55.000000Z",
      "updated_at": "2026-08-05T18:23:55.000000Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 15,
    "total": 1
  }
}
POST/webhook-subscriptions

Cadastrar Webhook

Cadastra uma nova URL de webhook para o seu merchant. A URL deve usar http ou https e não pode apontar para endereços privados ou internos (ex: localhost, 127.0.0.1, redes 10.x.x.x e 192.168.x.x).

{
  "url": "https://sua-aplicacao.com/webhooks/reportei",
  "events": ["customer_integration.status_updated", "customer_integration.deleted"],
  "is_active": true
}
PUT/webhook-subscriptions/{id}

Atualizar Webhook

Atualiza parcialmente um webhook já cadastrado. Envie apenas os campos que deseja alterar.

{
  "is_active": false
}
DELETE/webhook-subscriptions/{id}

Remover Webhook

Remove um webhook cadastrado do seu merchant.

{
  "success": true
}

Integrações Disponíveis

Explore as plataformas que podem ser integradas com a API do Reportei Connect: as integrações nativas, de marketing e redes sociais, das quais a API coleta métricas, e os toolkits, que executam ações nos apps do seu cliente.

Como usar as integrações nativas

1

Conecte a integração: use o endpoint de Customer Integrations para criar uma sessão e conectar a conta do cliente.

2

Descubra as métricas: chame GET /metrics/{integration_slug} para listar o que a plataforma oferece, ou use o Connect Explorer para testar a coleta sem escrever código.

3

Consulte os dados: use os endpoints /metrics/get-data ou /metrics/get-data-async com o payload copiado.

Os toolkits seguem outro caminho: você conecta o cliente com toolkit_slug e executa as tools com POST /tools/{tool_slug}/execute. O passo a passo está na seção Toolkits.

Lista de integrações (156)

Nome e slug de cada integração. Nas nativas, o slug é o integration_slug das métricas. Nos toolkits, marcados com a etiqueta Toolkit, é o toolkit_slug, e a lista atual vem em GET /toolkits.

156 integrações encontradas

Facebook
Facebook
facebook
Meta Ads
Meta Ads
facebook_ads
Instagram
Instagram
instagram
Instagram Business
Instagram Business
instagram_business
Threads
Threads
threads
Google Analytics
Google Analytics
google_analytics
Google Analytics 4
Google Analytics 4
google_analytics_4
Google Ads
Google Ads
google_adwords
Google Search Console
Google Search Console
search_console
Google My Business
Google My Business
google_my_business
Google Sheets
Google Sheets
google_sheets
Google Local Services
Google Local Services
google_local_services
YouTube
YouTube
youtube
LinkedIn
LinkedIn
linkedin
Linkedin Personal
Linkedin Personal
linkedin_personal
LinkedIn Ads
LinkedIn Ads
linkedin_ads
Twitter
Twitter
twitter
Twitter Ads
Twitter Ads
twitter_ads
TikTok
TikTok
tiktok
TikTok Business
TikTok Business
tiktok_business
TikTok Ads
TikTok Ads
tiktok_ads
Pinterest
Pinterest
pinterest
Pinterest Ads
Pinterest Ads
pinterest_ads
Microsoft Ads
Microsoft Ads
microsoft_ads
Spotify Ads
Spotify Ads
spotify_ads
Kwai Ads
Kwai Ads
kwai_ads
ChatGPT Ads
ChatGPT Ads
chatgpt_ads
RD Station Marketing
RD Station Marketing
rdstation
RD Station CRM
RD Station CRM
rd_crm
HubSpot Marketing
HubSpot Marketing
hubspot_marketing
HubSpot CRM
HubSpot CRM
hubspot_crm
Pipedrive
Pipedrive
pipedrive
Active Campaign
Active Campaign
active_campaign
Mailchimp
Mailchimp
mailchimp
Egoi
Egoi
egoi
Kommo
Kommo
kommo
Phonetrack
Phonetrack
phonetrack
Klaviyo
Klaviyo
klaviyo
GoHighLevel
GoHighLevel
go_high_level
CallRail
CallRail
callrail
Twilio
Twilio
twilio
Keyword.com
Keyword.com
keyword
Ahrefs
Ahrefs
ahrefs
GreatPages
GreatPages
great_pages
Shopify
Shopify
shopify
WooCommerce
WooCommerce
woo_commerce
NuvemShop
NuvemShop
nuvem_shop
Hotmart
Hotmart
hotmart
Eduzz
Eduzz
eduzz
Kiwify
Kiwify
kiwify
Magazord
Magazord
magazord
Olist
Olist
olist
iFood
iFood
ifood
Repediu
Repediu
repediuapi
Goomer
Goomer
goomer
Cardapio Web
Cardapio Web
cardapio_web
Acuity Scheduling
Acuity Scheduling
acuityToolkit
Agendor
Agendor
agendorToolkit
Asaas
Asaas
asaasToolkit
Asana
Asana
asanaToolkit
Attio
Attio
attioToolkit
AWeber
AWeber
aweberToolkit
Baremetrics
Baremetrics
baremetricsToolkit
beehiiv
beehiiv
beehiivToolkit
Bitly
Bitly
bitlyToolkit
Brevo
Brevo
brevoToolkit
Calendly
Calendly
calendlyToolkit
Canva
Canva
canvaToolkit
Capsule CRM
Capsule CRM
capsuleToolkit
Chatwoot
Chatwoot
chatwootToolkit
ClickUp
ClickUp
clickupToolkit
Clockify
Clockify
clockifyToolkit
Cloudflare
Cloudflare
cloudflareToolkit
Constant Contact
Constant Contact
constant_contactToolkit
Conta Azul
Conta Azul
conta_azulToolkit
Crisp
Crisp
crispToolkit
Discord
Discord
discordToolkit
Dropbox
Dropbox
dropboxToolkit
Dub
Dub
dubToolkit
Eventbrite
Eventbrite
eventbriteToolkit
Everhour
Everhour
everhourToolkit
Fathom Analytics
Fathom Analytics
fathomToolkit
Figma
Figma
figmaToolkit
folk
folk
folkToolkit
FreshBooks
FreshBooks
freshbooksToolkit
Freshdesk
Freshdesk
freshdeskToolkit
Front
Front
frontToolkit
GetResponse
GetResponse
getresponseToolkit
GitHub
GitHub
githubToolkit
GitLab
GitLab
gitlabToolkit
Gmail
Gmail
gmailToolkit
Google Calendar
Google Calendar
google_calendarToolkit
Google Chat
Google Chat
google_chatToolkit
Google Docs
Google Docs
google_docsToolkit
Google Drive
Google Drive
google_driveToolkit
Google Forms
Google Forms
google_formsToolkit
Google Tag Manager
Google Tag Manager
google_tag_managerToolkit
Google Tasks
Google Tasks
google_tasksToolkit
Grafana Cloud
Grafana Cloud
grafanaToolkit
Gumroad
Gumroad
gumroadToolkit
Instantly
Instantly
instantlyToolkit
Intercom
Intercom
intercomToolkit
Jira
Jira
jiraToolkit
Jotform
Jotform
jotformToolkit
Keap
Keap
keapToolkit
Kit
Kit
kitToolkit
Lemon Squeezy
Lemon Squeezy
lemon_squeezyToolkit
Mailjet
Mailjet
mailjetToolkit
Make
Make
makeToolkit
Manychat
Manychat
manychatToolkit
Melhor Envio
Melhor Envio
melhor_envioToolkit
Mention
Mention
mentionToolkit
Mercado Livre
Mercado Livre
mercado_livreToolkit
Mercado Pago
Mercado Pago
mercado_pagoToolkit
Microsoft Teams
Microsoft Teams
microsoft_teamsToolkit
Miro
Miro
miroToolkit
Mixpanel
Mixpanel
mixpanelToolkit
Netlify
Netlify
netlifyToolkit
Notion
Notion
notionToolkit
Omnisend
Omnisend
omnisendToolkit
OneDrive
OneDrive
onedriveToolkit
Outlook Calendar
Outlook Calendar
outlook_calendarToolkit
Outlook Mail
Outlook Mail
outlookToolkit
PagerDuty
PagerDuty
pagerdutyToolkit
PandaDoc
PandaDoc
pandadocToolkit
PostHog
PostHog
posthogToolkit
Printful
Printful
printfulToolkit
Printify
Printify
printifyToolkit
Recharge
Recharge
rechargeToolkit
Salesflare
Salesflare
salesflareToolkit
Salesloft
Salesloft
salesloftToolkit
SE Ranking
SE Ranking
se_rankingToolkit
Segment
Segment
segmentToolkit
Sentry
Sentry
sentryToolkit
SharePoint
SharePoint
sharepointToolkit
Slack
Slack
slackToolkit
Stripe
Stripe
stripeToolkit
Supabase
Supabase
supabaseToolkit
Superhuman Docs
Superhuman Docs
codaToolkit
SurveyMonkey
SurveyMonkey
surveymonkeyToolkit
Tally
Tally
tallyToolkit
Teamwork.com
Teamwork.com
teamworkToolkit
Todoist
Todoist
todoistToolkit
Toggl Track
Toggl Track
togglToolkit
Typeform
Typeform
typeformToolkit
Vercel
Vercel
vercelToolkit
Vimeo
Vimeo
vimeoToolkit
Webex
Webex
webexToolkit
Webflow
Webflow
webflowToolkit
WhatsApp Cloud API
WhatsApp Cloud API
whatsappToolkit
Wise
Wise
wiseToolkit
Wistia
Wistia
wistiaToolkit
WordPress.com
WordPress.com
wordpress_comToolkit
Wrike
Wrike
wrikeToolkit
Zendesk Sell
Zendesk Sell
zendesk_sellToolkit
Zoom
Zoom
zoomToolkit

Implemente o Connect com IA

Copie o prompt e cole no seu agente de código, como Claude Code, Cursor ou Codex. Ele cria a conta, lê a especificação e implementa a integração no seu projeto. Você só preenche o cadastro quando ele pedir.

Prompt para o seu agente

Implemente o Reportei Connect seguindo as instruções em https://app.connect.reportei.com/provision-trial.md. Leia o documento inteiro antes de começar.