Integração Open Delivery
Documentação técnica completa da nossa API Open Delivery. A integração segue a especificação
da ABRASEL — quem já implementou o padrão em outra plataforma logística reutiliza o mesmo código,
mudando só URL base, client_id e client_secret.
Introdução
A API Open Delivery do Entregas Expressas permite que softwares baseados na especificação Open Delivery se integrem à nossa plataforma sem novos desenvolvimentos. O fluxo típico:
- O estabelecimento (cliente final) gera
client_id+client_secretno painel dele e te envia. - Você troca essas credenciais por um access token via OAuth2 (client credentials).
- Você cria entregas, simula preços, marca pronto pra coleta, cancela — tudo com o token no header.
- Recebe webhooks em cada mudança de status (aceito, coletado, entregue, etc.).
Trocar informações com o estabelecimento
🔁 Você envia para o estabelecimento
- Merchant ID — o identificador do estabelecimento no seu sistema (UUID recomendado, até 100 chars).
- Webhook URL — URL onde você vai receber os eventos de atualização.
🔐 O estabelecimento envia para você
- Client ID — gerado no painel do Entregas Expressas.
- Client Secret — trate como senha, nunca exponha no front-end.
Cada estabelecimento gera um par próprio de credenciais — NÃO compartilhe credenciais entre estabelecimentos diferentes. O cliente preenche tudo em Integrações > Open Delivery e marca “ativar integração”.
URL base & APP-ID
URL base padrão:
https://entregasexpressas.com.br/api/opendelivery
Nosso APP-ID:
d3e3eaf2-e585-4177-962a-7be998efab8e
🔑 Autenticação
Padrão OAuth 2.0 client_credentials. Gera um access token JWT que vale por 24h
(expires_in: 86400). Use o token no header Authorization: Bearer <token>
de todas as chamadas seguintes.
Request body
{
"grant_type": "client_credentials",
"client_id": "<client_id_do_estabelecimento>",
"client_secret": "<client_secret_do_estabelecimento>"
}
Response 200
{
"access_token": "eyJhbGciOiJIUzI1NiI...",
"token_type": "bearer",
"expires_in": 86400
}
Exemplo cURL
curl -X POST https://entregasexpressas.com.br/api/opendelivery/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "...",
"client_secret": "..."
}'
📊 Simular entrega (disponibilidade e preço)
Antes de criar uma entrega, simule preço, ETAs e disponibilidade de veículos. Útil pra mostrar o valor da entrega no checkout do estabelecimento antes do consumidor finalizar.
Request body — campos principais
| Campo | Tipo | Descrição |
|---|---|---|
pickupAddress obrigatório | object · AddressLogistics | Endereço de coleta no estabelecimento. |
deliveryAddress obrigatório | object · AddressLogistics | Endereço de entrega ao consumidor. |
vehicle obrigatório | object · Vehicle | Tipos de veículo aceitos. |
limitTimes obrigatório | object | pickupLimit e deliveryLimit em minutos. |
totalOrderPrice obrigatório | object · Price | Valor total do pedido apresentado ao consumidor. |
totalWeight obrigatório | integer | Peso total em gramas. |
returnToMerchant obrigatório | boolean | Entregador volta ao ponto de coleta após entregar? |
canCombine obrigatório | boolean | Permitir agrupar com outros pedidos do mesmo entregador? |
merchant opcional | object | id + name do estabelecimento. |
orderDeliveryFee opcional | object · Price | Taxa de entrega já cobrada do consumidor. |
packageVolume opcional | number | Volume em m³. |
packageQuantity opcional | integer | Quantidade de volumes. |
additionalPricePercentual opcional | number | % acima do preço calculado que aceita pagar (ex.: 50 = 50%). |
specialInstructions opcional | string | Instruções livres para o entregador. |
onlinePayment opcional | boolean | Pagamento já foi feito no app do estabelecimento? |
Request body — exemplo
{
"merchant": {
"id": "22815773000169-dbc7e35a-c936-4665-9e13-eb55eb8b6824",
"name": "Pizza Plaza"
},
"pickupAddress": {
"country": "BR", "state": "BR-SP", "city": "São Paulo",
"district": "Moema", "street": "Av. Plaza", "number": "100",
"postalCode": "20111-000", "complement": "",
"latitude": -23.54809, "longitude": -46.63638
},
"deliveryAddress": {
"country": "BR", "state": "BR-SP", "city": "São Paulo",
"district": "Vila Mariana", "street": "R. Domingos de Morais", "number": "1267",
"postalCode": "04009-002", "complement": "Apto 31",
"latitude": -23.59045, "longitude": -46.63876
},
"vehicle": {
"type": ["MOTORBIKE_BAG"],
"container": "THERMIC",
"containerSize": "MEDIUM"
},
"limitTimes": {
"pickupLimit": 20,
"deliveryLimit": 45,
"orderCreatedAt": "2026-05-17T14:32:00Z"
},
"totalOrderPrice": { "value": 89.90, "currency": "BRL" },
"totalWeight": 1200,
"returnToMerchant": false,
"canCombine": true,
"onlinePayment": true
}
Response 200 — campos retornados
| Campo | Tipo | Descrição |
|---|---|---|
deliveryPrice | object | Valor calculado da entrega. |
vehicles | object | availableVehicles, vehiclesAvailable, nextAvailableVehicle (em minutos). |
ETAs | object | Tempos previstos (pickup & delivery, online ou offline). |
{
"deliveryPrice": { "value": 12.50, "currency": "BRL" },
"vehicles": {
"availableVehicles": 4,
"nextAvailableVehicle": 0
},
"ETAs": {
"updateMethod": "ONLINE",
"pickupEtaInMinutes": 8,
"pickupEtaDatetime": "2026-05-17T14:40:00Z",
"deliveryEtaInMinutes": 32,
"deliveryEtaDatetime": "2026-05-17T15:04:00Z",
"maxDeliveryTime": "2026-05-17T15:17:00Z"
}
}
📦 Criar nova entrega
Registra uma nova solicitação de entrega. Retorna 202 Accepted — o aceite final
chega via webhook (ACCEPTED ou REJECTED).
Request body — campos obrigatórios
Todos os campos do /availability mais:
| Campo | Tipo | Descrição |
|---|---|---|
orderId obrigatório | string (UUID) | ID do pedido gerado pelo estabelecimento. |
orderDisplayId obrigatório | string | ID visível ao consumidor (ex.: 96b41025). |
merchant obrigatório | object | id e name do estabelecimento. |
customerName obrigatório | string | Nome do destinatário. |
payments obrigatório | object | Veja abaixo. |
customerPhone opcional | string | Telefone do destinatário (precisa de consentimento). |
metadata opcional | object | Código de retirada (metadata.pickUpCode) e dados do marketplace de origem (metadata.partnerData). Veja Pedidos do iFood. |
notifyReadyForPickup opcional | boolean | Esperar o endpoint /readyForPickup antes de chamar o entregador. |
confirmationCodeRequired opcional | boolean | Exigir código de confirmação na entrega. |
items opcional | array | Lista { name, quantity } dos produtos. |
combinedOrdersIds opcional | array of UUIDs | Pedidos já em curso que podem ser agrupados. |
Objeto payments
| Campo | Tipo | Descrição |
|---|---|---|
method | enum | OFFLINEONLINE |
wirelessPos | boolean | Entregador leva maquininha? |
offlineMethod[].type | enum | CREDITDEBITMEAL_VOUCHERFOOD_VOUCHERPIXCASHCREDIT_DEBITOTHER |
offlineMethod[].amount | object · Price | Valor pago naquele método. |
change | object · Price | Troco (se type=CASH). |
Response 202 — request aceita pra processamento
{
"deliveryId": "8f3e9c20-aa14-4f5d-92b2-9bca6e0a3e10",
"orderId": "52122852-6626-11ed-9022-0242ac120002",
"status": "PENDING",
"createdAt": "2026-05-17T14:32:05Z"
}
ACCEPTED ou REJECTED) chega via
webhook. Caso seja REJECTED, o evento traz rejectionInfo.reason
(PRICE_EXCEEDED, VEHICLE_NOT_AVAILABLE, NO_DELIVERYPERSON_AVAILABLE,
DOES_NOT_MEET_REQUESTED_TIMES, REGION_NOT_SERVED, OTHER).
🍔 Pedidos do iFood (e de outros marketplaces)
Quando o pedido nasceu num marketplace e o seu sistema repassa a entrega para nós, informe a origem
e os códigos dele no objeto metadata de POST /v1/logistics/delivery.
É com eles que o entregador finaliza a entrega no iFood e que o estabelecimento encontra o pedido
no painel dele.
Campos de metadata
| Campo | Tipo | Descrição |
|---|---|---|
metadata.partnerData.orderPartnerName | string | Nome do marketplace de origem. Para o iFood, envie exatamente iFood (com essa grafia de maiúsculas e minúsculas). Esse valor identifica o pedido como do iFood. |
metadata.partnerData.orderDisplayId | string | Número do pedido no iFood, como aparece para a loja e para o consumidor. |
metadata.partnerData.phoneNumber | string | Telefone de contato do iFood com o localizador do pedido. O localizador tem 8 dígitos e precisa vir precedido de localizador, por exemplo 0800 200 5011, localizador: 93898428. |
metadata.pickUpCode | string | Código de retirada que o entregador apresenta na coleta. |
O que fazemos com cada código
- Localizador: ao concluir a entrega, o app do entregador mostra o código de 8 dígitos com um atalho para a página de confirmação de entrega do iFood. Se o texto não trouxer o localizador nesse formato, a entrega segue normalmente, só sem esse atalho.
- Número do pedido, telefone e código de retirada: aparecem nas observações da entrega, no painel e no app do entregador.
- O
orderIde oorderDisplayIdda raiz continuam sendo os IDs do seu sistema. Não troque pelos IDs do iFood: é peloorderIdque evitamos entrega duplicada e que você consulta, cancela e marca pronto para coleta.
Exemplo
{
"orderId": "52122852-6626-11ed-9022-0242ac120002",
"orderDisplayId": "4821",
"merchant": { "id": "loja-123", "name": "Hamburgueria Central" },
...
"metadata": {
"pickUpCode": "7391",
"partnerData": {
"orderPartnerName": "iFood",
"orderDisplayId": "6512",
"phoneNumber": "0800 200 5011, localizador: 93898428"
}
}
}
202 e "event": "REJECTED",
e a entrega não é criada. Pedido sem metadata.partnerData é tratado como pedido próprio da loja.
🛎️ Informar pronto para coleta
Use quando o pedido fica pronto antes do tempo previsto e você quer antecipar a chamada do entregador.
Só obrigatório se você setou notifyReadyForPickup: true ao criar a entrega.
Sem body. orderId na URL é o mesmo UUID enviado em POST /v1/logistics/delivery.
❌ Cancelar entrega
Pode ser chamado a qualquer momento. Se o entregador já aceitou, a operadora pode cobrar taxa adicional conforme política. Se o pedido estiver em entrega combinada, ele é desvinculado (os outros pedidos seguem normalmente).
Request body
| Campo | Tipo | Descrição |
|---|---|---|
reason obrigatório | enum | CONSUMER_CANCELLATION_REQUESTEDNO_SHOWPROBLEM_AT_MERCHANTHIGH_ACCEPTANCE_TIMEINCORRECT_ORDER_OR_PRODUCT_PICKUPPROBLEM_RESOLUTIONDISCOMBINE_ORDEROTHER |
action opcional | enum | RETURN_TO_STORECANCEL_DELIVERY |
message opcional | string | Texto livre com detalhes. |
Response 202
{
"additionalCharges": false
}
additionalCharges indica se o cancelamento gera cobrança adicional.
🔍 Detalhes da entrega
Consulta o estado completo de uma entrega — última tentativa processada (em caso de re-tentativas).
Retorna um objeto com todos os campos do DeliveryOrder (que você enviou),
mais o histórico de eventos, dados do entregador (deliveryMan) e localização (geoLocalization)
quando disponíveis.
📬 Webhook de status
A cada mudança de estado da entrega, fazemos um POST para a Webhook URL
configurada pelo estabelecimento. Responda com HTTP 204 em até 10s — qualquer
outra coisa dispara retry com backoff.
Importante: enviamos o POST para a Webhook URL exatamente
como cadastrada, sem concatenar nenhum sufixo de rota. Se o seu endpoint espera receber
os eventos em /v1/newLogisticEvent, cadastre esse caminho já incluído na
própria Webhook URL (ex.: https://seu-dominio/v1/opendelivery/webhook/v1/newLogisticEvent).
Headers obrigatórios
| Header | Conteúdo |
|---|---|
X-App-Id | Nosso APP-ID (UUID) — sempre o mesmo, identifica o serviço logístico. |
X-App-MerchantId | O merchant.id que vocês compartilharam. |
X-App-Signature | HMAC-SHA256 do body usando o client_secret como chave. Valide pra garantir autenticidade. |
Payload — campos principais
O tipo, o horário e a mensagem do evento vão aninhados dentro do objeto
event (não no topo). Os campos abaixo são sempre enviados; os
campos operacionais (vehicle, deliveryPrice, eta,
deliveryPerson) só aparecem em eventos de andamento — não
em PENDING, REJECTED nem CANCELLED.
A estrutura segue o schema oficial newLogisticEvent do OpenDelivery. Os campos
operacionais (vehicle, deliveryPrice, eta,
deliveryPerson) são condicionais: acompanham eventos de andamento,
mas não PENDING, REJECTED nem CANCELLED.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
deliveryId | string <UUID> | obrigatório | Id da entrega gerado pelo serviço logístico. |
orderId | string <uuid> | obrigatório | O orderId que vocês enviaram na criação (external id do endereço). |
orderDisplayId | string | obrigatório | Id do pedido exibido pro cliente na aplicação de pedidos. |
merchant.id | string [36..100] | obrigatório | Identificador único do merchant. Ecoamos o valor enviado na criação daquele endereço. |
merchant.name | string <=500 | obrigatório | Nome público do merchant. |
event.type | enum | obrigatório | PENDINGACCEPTEDREJECTED PICKUP_ONGOINGARRIVED_AT_MERCHANTORDER_PICKED DELIVERY_ONGOINGARRIVED_AT_CUSTOMERORDER_DELIVERED RETURNING_TO_MERCHANTRETURNED_TO_MERCHANT DELIVERY_FINISHEDCANCELLED |
event.datetime | string <date-time> | obrigatório | Quando o evento ocorreu (ISO 8601 UTC). |
event.message | string | opcional | Mensagem livre com detalhes do evento. |
event.rejectionInfo | object | opcional | Acompanha eventos REJECTED/CANCELLED. Traz reason com o motivo. |
problem | array<object> | opcional | Lista de problemas da entrega. Cada item: { reason(enum), message?, datetime, canFinishDeliver(bool), needMerchantAction(bool) }. |
customerName | string | obrigatório | Nome do cliente do endereço. |
vehicle | object | condicional | { type(enum), container(enum), containerSize?(enum), instruction?(string) }. |
deliveryPrice | object | condicional | { price: { value, currency }, pricingList(NORMAL|DYNAMIC), additionalPricePercentual }. |
eta | object | condicional | { updateMethod(OFFLINE|ONLINE), pickupEtaInMinutes, pickupEtaDatetime, deliveryEtaInMinutes, deliveryEtaDatetime, maxDeliveryTime }. |
deliveryPerson | object | condicional | Quando há entregador atribuído. { id, name, pictureURL?, phone? }. |
externalTrackingURL | string <URI> | opcional | Link público de acompanhamento da entrega. |
combinedOrdersIds | array<uuid> | opcional | Ids de outros pedidos do mesmo merchant levados pelo mesmo entregador. |
Exemplo — evento de aceite
{
"deliveryId": "32a20413-d5da-4b5d-a7f1-7a36e7f3495f",
"orderId": "2003",
"orderDisplayId": "12",
"merchant": { "id": "2001", "name": "Loja Exemplo" },
"event": {
"type": "ACCEPTED",
"datetime": "2026-05-17T14:32:18.000000Z",
"message": "Pedido aceito pelo serviço de entrega"
},
"problem": [],
"customerName": "João da Silva",
"vehicle": {
"type": "MOTORBIKE_BAG",
"container": "THERMIC",
"containerSize": "MEDIUM",
"instruction": "N/A"
},
"deliveryPrice": {
"price": { "value": 12.5, "currency": "BRL" },
"pricingList": "NORMAL",
"additionalPricePercentual": 0
},
"eta": {
"updateMethod": "OFFLINE",
"pickupEtaInMinutes": 15,
"pickupEtaDatetime": "2026-05-17T14:47:18.000000Z",
"deliveryEtaInMinutes": 30,
"deliveryEtaDatetime": "2026-05-17T15:02:18.000000Z",
"maxDeliveryTime": "2026-05-17T16:02:18.000000Z"
},
"deliveryPerson": {
"id": "1138",
"name": "Carlos Silva",
"phone": "5511999990000",
"pictureURL": "https://cdn.exemplo.com/couriers/1138.jpg"
},
"externalTrackingURL": "https://seu-dominio/tracking/32a20413-d5da-4b5d-a7f1-7a36e7f3495f",
"combinedOrdersIds": []
}
Exemplo — evento de rejeição
{
"deliveryId": "32a20413-d5da-4b5d-a7f1-7a36e7f3495f",
"orderId": "2003",
"orderDisplayId": "12",
"merchant": { "id": "2001", "name": "Loja Exemplo" },
"event": {
"type": "REJECTED",
"datetime": "2026-05-17T14:33:02.000000Z",
"message": "Pedido fora do raio de entrega",
"rejectionInfo": { "reason": "REGION_NOT_SERVED" }
},
"problem": [],
"customerName": "João da Silva",
"externalTrackingURL": "https://seu-dominio/tracking/32a20413-d5da-4b5d-a7f1-7a36e7f3495f",
"combinedOrdersIds": []
}
Validando a assinatura (Node.js)
const crypto = require('crypto');
function validateSignature(rawBody, headerSignature, clientSecret) {
const expected = crypto
.createHmac('sha256', clientSecret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(headerSignature, 'hex')
);
}
Schemas auxiliares
AddressLogistics
| Campo | Tipo | Obs. |
|---|---|---|
country obrigatório | string | ISO 3166-1 alpha-2 (ex.: BR). |
state obrigatório | string | ISO 3166-2 (ex.: BR-SP). |
city obrigatório | string | |
district obrigatório | string | Bairro. |
street obrigatório | string | |
number obrigatório | string | |
postalCode obrigatório | string | CEP, com ou sem hífen. |
complement obrigatório | string | Pode ser vazio mas o campo deve existir. |
reference opcional | string | Ponto de referência. |
latitude | number | -90 a 90. |
longitude | number | -180 a 180. |
Vehicle
| Campo | Tipo | Obs. |
|---|---|---|
type obrigatório | array<enum> | Tipos aceitos: MOTORBIKE_BAGMOTORBIKE_BOXCARBICYCLESCOOTERVUC. Mande mais de um pra deixar a operadora escolher. |
container obrigatório | enum | NORMALTHERMIC |
containerSize opcional | enum | SMALLMEDIUMLARGEEXTRA_LARGE |
instruction opcional | string | Instruções especiais ao entregador. |
Price
| Campo | Tipo | Obs. |
|---|---|---|
value obrigatório | number | Aceita até 4 casas decimais. |
currency obrigatório | string | Use BRL. |
Códigos de erro
| HTTP | Quando |
|---|---|
| 200/202 | Sucesso. 202 indica processamento assíncrono (estado final via webhook). |
| 400 | Request mal formada — JSON inválido ou faltando campos obrigatórios. |
| 401 | Token ausente, inválido ou expirado. |
| 403 | Token válido mas sem permissão para o recurso. |
| 404 | orderId não encontrado. |
| 422 | Conteúdo válido mas regra de negócio impede (ex.: cancelar pedido já entregue). |
| 503 | Serviço temporariamente indisponível — repita com backoff. |
Usar o painel do desenvolvedor
Todo o ciclo de desenvolvimento acontece dentro do portal: você cria o app, recebe credenciais sandbox, configura webhook, inspeciona requests e simula pedidos sem precisar coordenar com entregador real. Os passos abaixo seguem a ordem natural do trabalho.
1. Criar um app OpenDelivery
No painel, clique em Aplicativos > Novo aplicativo e escolha o tipo OpenDelivery. Assim que o app é criado, provisionamos automaticamente um estabelecimento sandbox numa praça dedicada, com a integração já ativada. Não há etapa manual de “conectar com cliente” — o sandbox existe pra você desenvolver sem depender de cliente real.
2. Onde pegar o client_id e client_secret de teste
- Abra o app no painel.
- Vá na aba Credenciais.
- Copie os campos
client_ideclient_secret— botão de copiar ao lado de cada um. - Use exatamente esses valores no payload de POST /oauth/token da seção de autenticação.
Precisa regerar o client_secret? Botão Regerar credenciais na própria aba
Credenciais. O par antigo deixa de funcionar imediatamente — atualize a sua aplicação antes de regerar.
3. Configurar a URL de webhook
- Aba Webhook do app.
- No campo URL de webhook, cole a URL HTTPS onde sua aplicação recebe eventos (formato detalhado em Webhook de status).
- Marque “Permitir que o estabelecimento sobrescreva esta URL” se você quer dar essa opção ao cliente final — em produção, alguns estabelecimentos têm webhook próprio (sistema de PDV, ERP). Se você deixar desmarcado, a URL fica fixa para todos.
- Clique em Salvar. A mudança vale imediatamente — não precisamos fazer deploy.
4. Inspecionar requests recebidas
A aba Inspeção mostra cada request que a nossa plataforma fez para a sua API (e, em alguns casos, o que sua API respondeu). Para cada uma:
- Método, path completo, status code e latência.
- Headers enviados e payload completo (request body).
- Filtros por endpoint e por status — útil pra ver só erros 4xx ou só webhooks.
Use isso pra debugar OAuth, validar payloads, achar a causa de um 4xx que sua aplicação retornou sem precisar mexer nos seus logs.
5. Simular pedidos e disparar webhooks
Aba Pedidos teste: lista os pedidos criados via API no estabelecimento sandbox. Para cada um, você consegue avançar o status manualmente (aceitar, atribuir entregador, marcar como coletado, entregue, ou disparar uma rejeição). Cada mudança envia o webhook real para a sua URL — você testa os handlers do seu lado sem precisar coordenar entregador real ou esperar minutos.
6. Convidar outros devs do time
- Aba Membros > Convidar.
- Digite o email do colega.
- Se ele já tem conta no portal, recebe um link pra aceitar. Se não tem, recebe link pra se cadastrar já vinculado ao seu app.
- Membros enxergam tudo: credenciais, webhook, inspeção, simulação.
7. Ajustar nome, descrição, logo e site do app
Aba Configurações: nome do app, descrição clara do que ele faz, site público, e logo (PNG/SVG). Esses dados aparecem na vitrine pública e na tela onde o estabelecimento ativa a sua integração — descrição clara e logo nítida têm efeito direto na conversão. Os três campos (descrição, site do app, logo) são obrigatórios para submeter o app à homologação.
Enviar para homologação
Depois que sua implementação estiver funcionando em sandbox, você submete o app à homologação da equipe Plataforma Entregas Expressas. Quando aprovado, o app fica visível para os +30 mil estabelecimentos que usam a plataforma — eles encontram seu app na vitrine pública e conectam pelo próprio painel, gerando credenciais reais.
Pré-requisitos antes de submeter
- Nome do app definitivo (esse é o nome que o estabelecimento vai ver).
- Descrição clara — o que sua integração faz, o que o estabelecimento ganha.
- Site do app — página pública onde o estabelecimento entende seu produto.
- Logo — vai aparecer na vitrine e na tela de ativação.
- OAuth funcionando: você consegue trocar
client_id+client_secretpor token em POST /oauth/token. - Criação de entrega testada em POST /delivery.
- Webhook sendo recebido, com assinatura HMAC validada do seu lado.
Como submeter
- Abra o app no painel.
- Na aba Visão geral, clique em Enviar para review.
- Se algum dos campos obrigatórios estiver vazio, o portal mostra exatamente o que falta antes de liberar o botão — preencha em Configurações e volte.
- O app entra no status “Aguardando review”. Você recebe email assim que a equipe avaliar.
O que muda após aprovação
- O app passa a aparecer na vitrine pública e na tela Integrações > Open Delivery que cada estabelecimento vê no painel dele.
- Cada estabelecimento que ativar sua integração gera o próprio par
client_id+client_secretno painel dele — sua aplicação precisa aceitar múltiplos pares e roteá-los pelo seumerchantId. - Apps oficialmente parceiros podem receber uma URL base própria (host dedicado). Solicite junto à equipe se for relevante pra você.
Se for rejeitado
O motivo aparece no painel (aba Visão geral) e por email. Os ajustes mais comuns são: descrição vaga, logo de baixa qualidade, webhook não validando assinatura, ou implementação parcial dos endpoints obrigatórios. Corrija e clique em Enviar para review novamente — não há limite de tentativas.