Documentação da API
API da Assinex
Compre assinaturas e produtos digitais automaticamente a partir do seu sistema. Consulte o catálogo com os seus preços, crie pedidos pagos com o saldo da carteira e entregue o acesso ao seu cliente pela resposta ou por webhook.
https://assinex.com/api/v1Authorization: Bearer <token>Como uma venda funciona
- 1Saldo
Você adiciona saldo por Pix no painel.
- 2Catálogo
GET /productsmostra preço e estoque. - 3Pedido
POST /ordersdebita o saldo e compra. - 4Entrega
O acesso vem na resposta ou no webhook.
#Primeiros passos
Em quatro passos você sai do cadastro para a primeira resposta da API.
-
Crie a conta e adicione saldo
Cadastre-se em assinex.com/cadastrar. No painel, abra Carteira › Adicionar saldo por Pix. O saldo entra assim que o pagamento é confirmado e é com ele que os pedidos são pagos.
-
Crie um token de API
Em Integração › Criar token, dê um nome (por exemplo, o nome do sistema) e copie o token na hora: ele começa com
ax_live_e aparece uma única vez. Cada conta pode ter até 10 tokens ativos.O token dá acesso ao seu saldo. Guarde em uma variável de ambiente do servidor. Nunca coloque no site, no app do celular ou no Git.
-
Faça a primeira chamada
Guarde o token no terminal e chame
GET /me. Ele não altera nada e confirma que a autenticação funciona.# Troque pelo token criado no painel. Não salve no Git. export ASSINEX_TOKEN="ax_live_SEU_TOKEN"curl https://assinex.com/api/v1/me \ -H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador. const response = await fetch("https://assinex.com/api/v1/me", { method: "GET", headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`, }, }); const data = await response.json(); if (!response.ok) { // data.error.code é estável; use-o para decidir o que fazer. throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`); } console.log(data);// npm install axios import axios from "axios"; const assinex = axios.create({ baseURL: "https://assinex.com/api/v1", headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` }, timeout: 30_000, }); try { const { status, data } = await assinex.get("/me"); console.log(status, data); } catch (error) { if (!error.response) throw error; // rede ou timeout const { code, message } = error.response.data.error; console.error(error.response.status, code, message); }<?php $ch = curl_init('https://assinex.com/api/v1/me'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'), ], CURLOPT_TIMEOUT => 30, ]); $resposta = curl_exec($ch); if ($resposta === false) { throw new RuntimeException(curl_error($ch)); // rede } $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $dados = json_decode($resposta, true); if ($status >= 400) { throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']); } print_r($dados);# pip install requests import os import requests resposta = requests.get( "https://assinex.com/api/v1/me", headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"}, timeout=30, ) dados = resposta.json() if not resposta.ok: erro = dados["error"] raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}") print(dados)// Java 11+ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class Assinex { public static void main(String[] args) throws Exception { String token = System.getenv("ASSINEX_TOKEN"); HttpRequest requisicao = HttpRequest.newBuilder() .uri(URI.create("https://assinex.com/api/v1/me")) .header("Authorization", "Bearer " + token) .timeout(Duration.ofSeconds(30)) .GET() .build(); HttpResponse<String> resposta = HttpClient.newHttpClient() .send(requisicao, HttpResponse.BodyHandlers.ofString()); // 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }. System.out.println(resposta.statusCode()); System.out.println(resposta.body()); } } -
Interprete a resposta
HTTP
200com o id e o nome da sua conta significa que está tudo certo. Qualquer outra situação volta como erro, sempre no mesmo formato: use ocodepara decidir o que fazer e mostre amessagesó para pessoas.{ "id": "acc_Z3k9QmX2pLr8Tw", "name": "Loja Exemplo", "status": "ativa", "created_at": "2026-09-30T12:00:00.000Z" }{ "error": { "code": "token_invalido", "message": "Token ausente, inválido ou revogado. Envie Authorization: Bearer <token>." } }
#Integre com IA
Use o Cursor, o Claude, o ChatGPT ou outra IA para escrever a integração no seu sistema. O prompt abaixo traz a referência completa da API e as regras que evitam os erros mais comuns (token exposto, pedido cobrado duas vezes, webhook sem verificação).
Carregando o prompt… Se não aparecer, abra https://assinex.com/llms.txt e copie o conteúdo.
A ferramenta consegue abrir links? Então basta este pedido curto:
Leia https://assinex.com/llms.txt e integre a API da Assinex ao meu projeto seguindo exatamente as regras desse arquivo. Antes de escrever código, me faça as perguntas indicadas nele.Como usar em cada ferramenta
Cursor
- Jeito mais rápido: abra o chat (Ctrl+L, ou Cmd+L no Mac), escolha o modo Agent, cole o prompt e envie.
- Para o projeto inteiro lembrar das regras: crie o arquivo
.cursor/rules/assinex.mdcna raiz do projeto e cole o prompt dentro. - Como documentação indexada: em Cursor Settings, na área de Docs, adicione
https://assinex.com/llms.txt. Depois escreva@Docse escolha Assinex no chat.
Claude
- No claude.ai: cole o prompt no início de uma conversa e depois mande o código ou a descrição do seu sistema.
- Em um Projeto do claude.ai: crie um projeto e cole o prompt nas instruções.
- No Claude Code (terminal): salve o prompt em
CLAUDE.mdna raiz do projeto, ou peça direto: “Leia https://assinex.com/llms.txt e integre a API da Assinex”.
ChatGPT
- Numa conversa: cole o prompt, envie e depois conte como é o seu sistema (linguagem, loja, onde ficam os pedidos).
- Em um Projeto: cole o prompt nas instruções do projeto para reaproveitar em várias conversas.
- No Codex ou em agentes que usam
AGENTS.md: salve o prompt emAGENTS.mdna raiz do repositório.
GitHub Copilot
- Salve o prompt em
.github/copilot-instructions.mdno seu repositório. - No Copilot Chat em modo agente, peça: “Integre a API da Assinex seguindo as instruções do repositório”.
Windsurf
- Jeito mais rápido: cole o prompt no Cascade e envie.
- Para o projeto inteiro: salve o prompt como regra do workspace em
.windsurf/rules/assinex.md.
Gemini
- No Gemini (web ou app): cole o prompt no início da conversa e depois descreva o seu sistema.
- Para reaproveitar: crie um Gem com o prompt nas instruções.
- No Gemini CLI: salve o prompt em
GEMINI.mdna raiz do projeto.
Lovable, Bolt e v0
Atenção: essas ferramentas geram sites que rodam no navegador. O token da Assinex não pode ficar no código do site, senão qualquer pessoa consegue usá-lo e gastar o seu saldo.
- Cole o prompt no chat da ferramenta.
- Peça: “Faça as chamadas para a Assinex numa função de servidor (por exemplo, uma Edge Function do Supabase) e guarde o token em Secrets, nunca no frontend”.
- Cadastre o token como segredo (Secrets ou Environment Variables) no painel da ferramenta, com o nome
ASSINEX_TOKEN.
Revise antes de publicar. Teste com GET /me e faça um pedido de valor baixo com um external_id de teste. Confirme que o token está só no servidor e que o webhook recusa assinaturas inválidas.
#Autenticação
Todas as rotas exigem um token de API no cabeçalho Authorization, no esquema Bearer. O token é criado no painel em Integração › Criar token, começa com ax_live_ e só aparece uma vez.
Authorization: Bearer ax_live_SEU_TOKEN- Cada token age em nome da conta que o criou: só enxerga o saldo, o catálogo e os pedidos dessa conta.
- Token revogado no painel para de funcionar na hora. Crie um token por sistema; se um vazar, revogue só ele.
- A API não usa cookies nem sessão do painel. Sem o cabeçalho, a resposta é
401 token_invalido. - Chame a API só a partir do seu servidor. Um token no navegador ou no app fica visível para qualquer pessoa.
#Base URL e ambientes
Todas as rotas ficam sob uma única base, sempre em HTTPS:
https://assinex.com/api/v1Não existe ambiente de testes (sandbox). Toda chamada usa a sua conta real. As consultas (GET) não alteram nada. POST /orders compra de verdade e debita o saldo. Para testar a integração, use GET /me e GET /products e faça um pedido de valor baixo com um external_id de teste.
#Formato das respostas
| Item | Regra |
|---|---|
| Corpo | JSON em UTF-8, nas respostas e no corpo de POST (com Content-Type: application/json, até 16 KB). |
| Valores | Números em reais (BRL), com duas casas decimais. Exemplo: 11.5 é R$ 11,50. |
| Datas | Texto ISO 8601 em UTC, como 2026-09-30T12:00:00.000Z. |
| Ids | Texto com prefixo: acc_ conta, prd_ produto, ord_ pedido, evt_ evento de webhook. Trate como texto opaco. |
| Cache | Toda resposta vem com Cache-Control: no-store. |
| Erros | Status HTTP de erro com { "error": { "code", "message" } }. Veja Códigos de erro. |
#Códigos de erro
Todo erro tem o mesmo formato. Use o code no seu sistema: ele é estável. A message é para pessoas e pode mudar.
{
"error": {
"code": "saldo_insuficiente",
"message": "Saldo insuficiente na carteira para esta compra."
}
}Como tratar
- 4xx: a requisição foi recusada e nada foi cobrado. Corrija o pedido antes de repetir (exceto
429, que pede só para esperar). - 5xx ou falha de rede: repita a mesma requisição. Em
POST /orders, com o mesmoexternal_id: não há cobrança dupla.
Todos os códigos
| HTTP | code | Quando acontece |
|---|---|---|
| 400 | external_id_invalido | external_id vazio, com mais de 100 caracteres ou com caracteres fora de letras, números e . _ : -. |
| 400 | external_id_obrigatorio | Faltou external_id. |
| 400 | json_invalido | O corpo não é um JSON válido. |
| 400 | product_id_obrigatorio | Faltou product_id (ou o Content-Type). |
| 400 | quantidade_invalida | quantity não é um inteiro entre 1 e max_per_order. |
| 401 | token_invalido | Token ausente, errado ou revogado. |
| 402 | saldo_insuficiente | A carteira não cobre o total. Nada é cobrado. |
| 403 | api_desativada | O acesso pela API foi desligado para esta conta. |
| 403 | conta_suspensa | A conta foi suspensa. Fale com a Assinex. |
| 403 | limite_diario_excedido | O pedido passaria do limite de gasto diário da conta (horário de Brasília). Nada é cobrado. |
| 404 | pedido_nao_encontrado | O pedido não existe nesta conta (ou o id não tem o formato ord_…). |
| 404 | produto_nao_encontrado | O product_id não existe no catálogo. |
| 404 | rota_nao_encontrada | Método ou caminho que não existe na API. |
| 409 | external_id_em_uso | O external_id já foi usado com outro produto ou outra quantidade. |
| 409 | produto_indisponivel | Sem estoque para a quantidade ou produto pausado. Tente mais tarde. |
| 413 | corpo_grande_demais | O corpo passou de 16 KB. |
| 429 | limite_de_requisicoes | Mais de 120 requisições por minuto no token, ou mais de 30 tentativas por minuto com token inválido no mesmo IP. |
| 500 | erro_interno | Falha do nosso lado. Repita a requisição; em POST /orders use o mesmo external_id. |
| 503 | vendas_pausadas | Vendas pausadas temporariamente. Nada é cobrado; repita mais tarde com o mesmo external_id. |
#Limites de requisição
| Limite | Valor |
|---|---|
| Requisições por token | 120 por minuto |
| Tentativas com token inválido | 30 por minuto por IP |
| Tokens ativos por conta | 10 |
| Tamanho do corpo | 16 KB |
Itens por página em GET /orders | 100 |
As respostas autenticadas trazem os cabeçalhos abaixo. Acima do limite, a resposta é 429 limite_de_requisicoes com Retry-After em segundos.
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Retry-After: 23A conta também pode ter um limite de gasto diário definido pela Assinex. Um pedido que passaria desse valor recebe 403 limite_diario_excedido, sem cobrança.
#Idempotência
O external_id de POST /orders é a chave do pedido na sua conta. Se a conexão cair ou vier um 500, repita exatamente a mesma requisição:
- Mesmo
external_id, mesmo produto e mesma quantidade: você recebe o pedido que já existe, com HTTP200e"idempotent_replay": true. Nada é cobrado de novo. - Mesmo
external_idcom outro produto ou outra quantidade:409 external_id_em_uso. - Duas requisições iguais ao mesmo tempo geram um só pedido.
Use um id que já existe no seu sistema, como o número da venda (loja-1042). Não gere um id aleatório a cada tentativa, ou cada repetição vira uma compra nova.
#Paginação
Só GET /orders é paginado. Os pedidos vêm do mais novo para o mais antigo, até limit por página (padrão 50, máximo 100). Quando a página vem cheia, next traz um cursor; envie esse valor em before para a próxima página. next: null indica o fim.
# Primeira página
curl "https://assinex.com/api/v1/orders?limit=100" \
-H "Authorization: Bearer $ASSINEX_TOKEN"
# { "data": [ ...100 pedidos... ], "next": "ord_3Fh8sK1pQw0LmZ" }
# Página seguinte: envie o next em before
curl "https://assinex.com/api/v1/orders?limit=100&before=ord_3Fh8sK1pQw0LmZ" \
-H "Authorization: Bearer $ASSINEX_TOKEN"GET /products devolve o catálogo inteiro de uma vez.
Referência
Endpoints
Seis operações, todas sob https://assinex.com/api/v1 e todas autenticadas. Os exemplos de cada uma estão em cURL, JavaScript, Node.js, PHP, Python e Java; a linguagem escolhida vale para a página inteira.
| Operação | Rota |
|---|---|
| Consultar a conta | GET /me |
| Consultar o saldo | GET /balance |
| Listar produtos | GET /products |
| Criar pedido | POST /orders |
| Consultar pedido | GET /orders/{id} |
| Listar pedidos | GET /orders |
Conta
#Consultar a conta
https://assinex.com/api/v1/meConfirma que o token funciona e mostra a conta dona dele.
Use como primeira chamada da integração e como verificação de saúde: se responder 200, o token é válido e a conta está ativa.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).
Resposta 200 OK
idtexto- Id público da conta (
acc_…). nametexto- Nome da conta.
statustexto- Sempre
ativa. Conta suspensa recebe403 conta_suspensa. created_atdata- Criação da conta, ISO 8601 em UTC.
Erros
Só os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl https://assinex.com/api/v1/me \
-H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/me", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
},
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.get("/me");
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/me');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.get(
"https://assinex.com/api/v1/me",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/me"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"id": "acc_Z3k9QmX2pLr8Tw",
"name": "Loja Exemplo",
"status": "ativa",
"created_at": "2026-09-30T12:00:00.000Z"
}{
"error": {
"code": "token_invalido",
"message": "Token ausente, inválido ou revogado. Envie Authorization: Bearer <token>."
}
}Carteira
#Consultar o saldo
https://assinex.com/api/v1/balanceSaldo disponível na carteira, em reais.
Os pedidos são pagos com este saldo. Para adicionar saldo, use Carteira › Adicionar saldo por Pix no painel: a API não cria cobranças.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).
Resposta 200 OK
balancenúmero- Saldo em reais, com duas casas decimais.
currencytexto- Sempre
BRL.
Erros
Só os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl https://assinex.com/api/v1/balance \
-H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/balance", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
},
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.get("/balance");
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/balance');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.get(
"https://assinex.com/api/v1/balance",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/balance"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"balance": 138.5,
"currency": "BRL"
}Catálogo
#Listar produtos
https://assinex.com/api/v1/productsCatálogo com o seu preço, estoque e limite por pedido.
Lista todo o catálogo de uma vez (sem paginação), já com o seu preço. Guarde o id: é ele que vai em product_id ao criar um pedido.
O estoque muda o tempo todo. Consulte antes de vender e trate available: false como temporário.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).
Resposta 200 OK
data[].idtexto- Id do produto (
prd_…). data[].nametexto- Nome do produto.
data[].descriptiontexto ou null- Descrição, quando houver.
data[].pricenúmero ou null- Seu preço unitário em reais.
nullquando ainda não há preço para a sua conta. data[].currencytexto- Sempre
BRL. data[].availablebooleano- Se dá para comprar agora.
data[].stockinteiro- Unidades em estoque neste momento.
data[].max_per_orderinteiro- Máximo de unidades em um pedido.
data[].imagetexto ou null- Caminho da imagem no domínio da Assinex (relativo, por exemplo
/media/catalog/prd_….webp?v=2). data[].marktexto ou null- Emoji que identifica o produto, quando houver.
Erros
Só os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl https://assinex.com/api/v1/products \
-H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/products", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
},
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.get("/products");
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/products');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.get(
"https://assinex.com/api/v1/products",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/products"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"data": [
{
"id": "prd_Kd8Xq2mLp0Za1w",
"name": "Assinatura digital 18 meses",
"description": null,
"price": 11.5,
"currency": "BRL",
"available": true,
"stock": 277,
"max_per_order": 50,
"image": "/media/catalog/prd_Kd8Xq2mLp0Za1w.webp?v=2",
"mark": null
}
]
}Pedidos
#Criar pedido
https://assinex.com/api/v1/ordersCompra um produto com o saldo da carteira.
O valor é debitado da carteira quando o pedido é criado. Na maioria das vezes a resposta já traz o acesso ("status": "entregue"). Se vier processando, espere o webhook ou consulte o pedido.
O external_id torna a chamada idempotente: repetir a mesma requisição devolve o mesmo pedido, com HTTP 200 e "idempotent_replay": true, sem cobrar de novo. Veja Idempotência.
Altera dados reais. Cria um pedido real e debita o valor do seu saldo. Não existe ambiente de testes.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).Content-Typetextoobrigatórioapplication/json. Sem ele o corpo é ignorado e a resposta éproduct_id_obrigatorio.
Corpo da requisição
product_idtextoobrigatório- O
idde GET /products. external_idtextoobrigatório- Um id seu, único por pedido (por exemplo, o número da venda na sua loja). De 1 a 100 caracteres: letras, números e
. _ : -. quantityinteiroopcional- Padrão
1. De 1 até omax_per_orderdo produto.
Resposta 201 Created
orderobjeto- O pedido. Veja o objeto pedido.
idempotent_replaybooleanotruequando oexternal_idjá existia e o pedido devolvido é o mesmo de antes (HTTP200).falsenum pedido novo (HTTP201).
Erros
| HTTP | code | Quando acontece |
|---|---|---|
| 400 | product_id_obrigatorio | Faltou product_id (ou o Content-Type). |
| 400 | external_id_obrigatorio | Faltou external_id. |
| 400 | external_id_invalido | external_id vazio, com mais de 100 caracteres ou com caracteres fora de letras, números e . _ : -. |
| 400 | quantidade_invalida | quantity não é um inteiro entre 1 e max_per_order. |
| 400 | json_invalido | O corpo não é um JSON válido. |
| 402 | saldo_insuficiente | A carteira não cobre o total. Nada é cobrado. |
| 403 | limite_diario_excedido | O pedido passaria do limite de gasto diário da conta (horário de Brasília). Nada é cobrado. |
| 404 | produto_nao_encontrado | O product_id não existe no catálogo. |
| 409 | produto_indisponivel | Sem estoque para a quantidade ou produto pausado. Tente mais tarde. |
| 409 | external_id_em_uso | O external_id já foi usado com outro produto ou outra quantidade. |
| 413 | corpo_grande_demais | O corpo passou de 16 KB. |
| 503 | vendas_pausadas | Vendas pausadas temporariamente. Nada é cobrado; repita mais tarde com o mesmo external_id. |
Também podem ocorrer os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl -X POST https://assinex.com/api/v1/orders \
-H "Authorization: Bearer $ASSINEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prd_Kd8Xq2mLp0Za1w",
"quantity": 1,
"external_id": "loja-1042"
}'// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
product_id: "prd_Kd8Xq2mLp0Za1w",
quantity: 1,
external_id: "loja-1042",
}),
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.post("/orders", {
product_id: "prd_Kd8Xq2mLp0Za1w",
quantity: 1,
external_id: "loja-1042",
});
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout: repita com o mesmo external_id
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'product_id' => 'prd_Kd8Xq2mLp0Za1w',
'quantity' => 1,
'external_id' => 'loja-1042',
]),
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede: repita com o mesmo external_id
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.post(
"https://assinex.com/api/v1/orders",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
json={
"product_id": "prd_Kd8Xq2mLp0Za1w",
"quantity": 1,
"external_id": "loja-1042",
},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
String corpo = "{\"product_id\":\"prd_Kd8Xq2mLp0Za1w\",\"quantity\":1,\"external_id\":\"loja-1042\"}";
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/orders"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(30))
.POST(HttpRequest.BodyPublishers.ofString(corpo))
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"order": {
"id": "ord_7Qx2kP9mLsT1aB",
"external_id": "loja-1042",
"product_id": "prd_Kd8Xq2mLp0Za1w",
"product_name": "Assinatura digital 18 meses",
"quantity": 1,
"unit_price": 11.5,
"total": 11.5,
"currency": "BRL",
"status": "entregue",
"delivery": {
"items": [
{
"type": "link",
"link": "https://exemplo.com/ativar/x8Kq",
"instructions": "Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
}
],
"text": "Link: https://exemplo.com/ativar/x8Kq\nComo usar: Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
},
"error": null,
"refunded": false,
"created_at": "2026-09-30T12:00:00.000Z",
"delivered_at": "2026-09-30T12:00:02.000Z",
"updated_at": "2026-09-30T12:00:02.000Z"
},
"idempotent_replay": false
}{
"error": {
"code": "saldo_insuficiente",
"message": "Saldo insuficiente na carteira para esta compra."
}
}{
"error": {
"code": "external_id_em_uso",
"message": "Esta external_id já foi usada em outro pedido com produto ou quantidade diferentes."
}
}Pedidos
#Consultar pedido
https://assinex.com/api/v1/orders/{id}Um pedido da sua conta, pelo id da Assinex.
Use para acompanhar um pedido processando quando você não usa webhooks. Consulte a cada 10 a 30 segundos.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Parâmetros de caminho
idtextoobrigatório- Id do pedido (
ord_…), devolvido ao criar o pedido.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).
Resposta 200 OK
orderobjeto- O pedido. Veja o objeto pedido.
Erros
| HTTP | code | Quando acontece |
|---|---|---|
| 404 | pedido_nao_encontrado | O pedido não existe nesta conta (ou o id não tem o formato ord_…). |
Também podem ocorrer os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl https://assinex.com/api/v1/orders/ord_7Qx2kP9mLsT1aB \
-H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/orders/ord_7Qx2kP9mLsT1aB", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
},
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.get("/orders/ord_7Qx2kP9mLsT1aB");
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/orders/ord_7Qx2kP9mLsT1aB');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.get(
"https://assinex.com/api/v1/orders/ord_7Qx2kP9mLsT1aB",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/orders/ord_7Qx2kP9mLsT1aB"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"order": {
"id": "ord_7Qx2kP9mLsT1aB",
"external_id": "loja-1042",
"product_id": "prd_Kd8Xq2mLp0Za1w",
"product_name": "Assinatura digital 18 meses",
"quantity": 1,
"unit_price": 11.5,
"total": 11.5,
"currency": "BRL",
"status": "entregue",
"delivery": {
"items": [
{
"type": "link",
"link": "https://exemplo.com/ativar/x8Kq",
"instructions": "Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
}
],
"text": "Link: https://exemplo.com/ativar/x8Kq\nComo usar: Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
},
"error": null,
"refunded": false,
"created_at": "2026-09-30T12:00:00.000Z",
"delivered_at": "2026-09-30T12:00:02.000Z",
"updated_at": "2026-09-30T12:00:02.000Z"
}
}{
"error": {
"code": "pedido_nao_encontrado",
"message": "Pedido não encontrado."
}
}Pedidos
#Listar pedidos
https://assinex.com/api/v1/ordersPedidos da conta, do mais novo para o mais antigo. Busca por external_id.
Para achar um pedido pelo seu id, filtre por external_id. Sem filtros, devolve os pedidos mais recentes primeiro.
A paginação usa cursor: quando a página vem cheia, next traz o id do último pedido; envie esse valor em before para buscar a página seguinte. next: null indica a última página. Veja Paginação.
Autenticação
Token de API no cabeçalho Authorization. A operação vale só para a conta dona do token. Veja Autenticação.
Parâmetros de consulta
external_idtextoopcional- O seu id do pedido. Valor fora do formato devolve
400 external_id_invalido. statustextoopcionalprocessando,entregueoufalhou. Outros valores são ignorados.Valores:processandoentreguefalhoulimitinteiroopcional- De 1 a 100. Padrão 50.
beforetextoopcional- Cursor: o valor de
nextda página anterior.
Cabeçalhos
AuthorizationtextoobrigatórioBearerseguido do seu token (ax_live_…).
Resposta 200 OK
datalista- Pedidos no formato do objeto pedido.
nexttexto ou null- Cursor da próxima página, ou
null.
Erros
| HTTP | code | Quando acontece |
|---|---|---|
| 400 | external_id_invalido | external_id fora do formato aceito. |
Também podem ocorrer os erros comuns de qualquer rota autenticada (401, 403, 429 e 500).
curl "https://assinex.com/api/v1/orders?external_id=loja-1042" \
-H "Authorization: Bearer $ASSINEX_TOKEN"// Rode no servidor (Node.js 18+, Bun, Deno, Edge Functions). Nunca no navegador.
const response = await fetch("https://assinex.com/api/v1/orders?external_id=loja-1042", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
},
});
const data = await response.json();
if (!response.ok) {
// data.error.code é estável; use-o para decidir o que fazer.
throw new Error(`${response.status} ${data.error.code}: ${data.error.message}`);
}
console.log(data);// npm install axios
import axios from "axios";
const assinex = axios.create({
baseURL: "https://assinex.com/api/v1",
headers: { Authorization: `Bearer ${process.env.ASSINEX_TOKEN}` },
timeout: 30_000,
});
try {
const { status, data } = await assinex.get("/orders", { params: { external_id: "loja-1042" } });
console.log(status, data);
} catch (error) {
if (!error.response) throw error; // rede ou timeout
const { code, message } = error.response.data.error;
console.error(error.response.status, code, message);
}<?php
$ch = curl_init('https://assinex.com/api/v1/orders?' . http_build_query([
'external_id' => 'loja-1042',
]));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException(curl_error($ch)); // rede
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status >= 400) {
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
print_r($dados);# pip install requests
import os
import requests
resposta = requests.get(
"https://assinex.com/api/v1/orders",
headers={"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"},
params={
"external_id": "loja-1042",
},
timeout=30,
)
dados = resposta.json()
if not resposta.ok:
erro = dados["error"]
raise RuntimeError(f"{resposta.status_code} {erro['code']}: {erro['message']}")
print(dados)// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Assinex {
public static void main(String[] args) throws Exception {
String token = System.getenv("ASSINEX_TOKEN");
HttpRequest requisicao = HttpRequest.newBuilder()
.uri(URI.create("https://assinex.com/api/v1/orders?external_id=loja-1042"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> resposta = HttpClient.newHttpClient()
.send(requisicao, HttpResponse.BodyHandlers.ofString());
// 2xx: sucesso. 4xx e 5xx: o corpo traz { "error": { "code", "message" } }.
System.out.println(resposta.statusCode());
System.out.println(resposta.body());
}
}{
"data": [
{
"id": "ord_7Qx2kP9mLsT1aB",
"external_id": "loja-1042",
"product_id": "prd_Kd8Xq2mLp0Za1w",
"product_name": "Assinatura digital 18 meses",
"quantity": 1,
"unit_price": 11.5,
"total": 11.5,
"currency": "BRL",
"status": "entregue",
"delivery": {
"items": [
{
"type": "link",
"link": "https://exemplo.com/ativar/x8Kq",
"instructions": "Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
}
],
"text": "Link: https://exemplo.com/ativar/x8Kq\nComo usar: Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
},
"error": null,
"refunded": false,
"created_at": "2026-09-30T12:00:00.000Z",
"delivered_at": "2026-09-30T12:00:02.000Z",
"updated_at": "2026-09-30T12:00:02.000Z"
}
],
"next": null
}{
"error": {
"code": "external_id_invalido",
"message": "external_id inválido."
}
}Objetos
#O objeto pedido
É o que vem em order (criar e consultar), em cada item de data (listar) e em data.order dos webhooks.
idtexto- Id do pedido na Assinex (
ord_…). external_idtexto- O seu id, enviado ao criar.
product_idtexto- Id do produto (
prd_…). product_nametexto- Nome do produto no momento da compra.
quantityinteiro- Unidades compradas.
unit_pricenúmero- Preço unitário cobrado, em reais.
totalnúmero- Total debitado da carteira.
currencytexto- Sempre
BRL. statustextoprocessando,entregueoufalhou. Veja Situação do pedido.deliveryobjeto ou null- O acesso entregue. Só vem com
status: entregue. Veja Entrega. errorobjeto ou null- Com
status: falhou:{ code, message }explicando o motivo. refundedbooleanotruequando o valor voltou para a carteira.created_atdata- Criação do pedido (ISO 8601, UTC).
delivered_atdata ou null- Quando o acesso foi liberado.
updated_atdata- Última mudança no pedido.
#Situação do pedido
| status | Significado | O que fazer |
|---|---|---|
processando | Pago com a carteira; o acesso está sendo liberado. | Espere o webhook ou consulte a cada 10 a 30 segundos. |
entregue | O acesso está em delivery. | Repasse ao seu cliente. |
falhou | A compra não pôde ser concluída. O motivo está em error; refunded: true indica que o valor voltou para a carteira. | Crie outro pedido com outro external_id, se quiser tentar de novo. |
Um pedido nunca sai de entregue, e só é estornado quando há certeza de que a compra não aconteceu.
#Entrega
delivery.items traz um item por unidade comprada. O campo type diz a forma do acesso, e instructions é o como usar: mostre esse texto ao seu cliente junto com o acesso, sem trocar os passos.
| type | Quando | Campos |
|---|---|---|
account | Conta pronta | login, password, instructions e, se houver, totp (código ou link do 2FA) |
link | Link para resgatar | link e instructions |
code | Código de resgate | code e instructions |
text | Outro formato | content (texto livre) e, quando houver, link, code e instructions |
delivery.text junta tudo com rótulos (Login:, Senha:, Link:…) e termina com a linha Como usar:. É o mesmo conteúdo do arquivo .txt baixado no painel, pronto para uma mensagem. Quando o item é uma conta, use login e password: o texto cru da conta não vem na resposta.
{
"type": "account",
"login": "[email protected]",
"password": "segredo123",
"instructions": "Entre em https://account.microsoft.com com o e-mail e a senha entregues. No primeiro acesso, troque a senha. O plano já está nessa conta."
}Webhooks
#Eventos
Configure a URL em Integração › Webhook. Ela precisa ser https e pública. Ao salvar pela primeira vez, você recebe um segredo whsec_… que aparece uma única vez; dá para gerar outro no mesmo lugar.
| Evento | Quando | data |
|---|---|---|
pedido.entregue | O acesso foi liberado. | { "order": … } |
pedido.falhou | A compra não saiu e o valor foi estornado. | { "order": … } |
teste | Botão Enviar teste no painel. | { "message": "Evento de teste da Assinex." } |
Cada evento é um POST com estes cabeçalhos e corpo (data.order segue o objeto pedido):
POST /sua-url HTTP/1.1
Content-Type: application/json
User-Agent: Assinex-Webhooks/1.0
X-Assinex-Event: pedido.entregue
X-Assinex-Delivery: evt_Hn2Lq8rVx1Ck9d
X-Assinex-Timestamp: 1790769602
X-Assinex-Signature: sha256=5f1c…{
"id": "evt_Hn2Lq8rVx1Ck9d",
"type": "pedido.entregue",
"created_at": "2026-09-30T12:00:02.000Z",
"data": {
"order": {
"id": "ord_7Qx2kP9mLsT1aB",
"external_id": "loja-1042",
"product_id": "prd_Kd8Xq2mLp0Za1w",
"product_name": "Assinatura digital 18 meses",
"quantity": 1,
"unit_price": 11.5,
"total": 11.5,
"currency": "BRL",
"status": "entregue",
"delivery": {
"items": [
{
"type": "link",
"link": "https://exemplo.com/ativar/x8Kq",
"instructions": "Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
}
],
"text": "Link: https://exemplo.com/ativar/x8Kq\nComo usar: Abra o link e conclua o resgate na conta em que você quer o benefício. Faça isso logo após a entrega: alguns links expiram."
},
"error": null,
"refunded": false,
"created_at": "2026-09-30T12:00:00.000Z",
"delivered_at": "2026-09-30T12:00:02.000Z",
"updated_at": "2026-09-30T12:00:02.000Z"
}
}
}{
"id": "evt_Pq4Ws9tYc2Hm5n",
"type": "pedido.falhou",
"created_at": "2026-09-30T12:04:10.000Z",
"data": {
"order": {
"id": "ord_7Qx2kP9mLsT1aB",
"external_id": "loja-1042",
"product_id": "prd_Kd8Xq2mLp0Za1w",
"product_name": "Assinatura digital 18 meses",
"quantity": 1,
"unit_price": 11.5,
"total": 11.5,
"currency": "BRL",
"status": "falhou",
"delivery": null,
"error": {
"code": "produto_indisponivel",
"message": "Produto indisponível no momento."
},
"refunded": true,
"created_at": "2026-09-30T12:00:00.000Z",
"delivered_at": null,
"updated_at": "2026-09-30T12:04:10.000Z"
}
}
}{
"id": "evt_Lm3Rt7uVb0Nx4q",
"type": "teste",
"created_at": "2026-09-30T12:10:00.000Z",
"data": {
"message": "Evento de teste da Assinex."
}
}Entrega e novas tentativas
Responda com qualquer 2xx em até 10 segundos. Sem 2xx, tentamos de novo em 1 minuto, 5 minutos, 15 minutos, 1 hora, 3 horas, 6 horas e 12 horas (8 tentativas no total). Pelo painel dá para ver cada envio e reenviar. Use o id do evento (o mesmo de X-Assinex-Delivery) para ignorar repetições.
#Verificar a assinatura
Calcule o HMAC-SHA256 do texto {X-Assinex-Timestamp}.{corpo bruto} com o seu segredo e compare com o valor depois de sha256=, em tempo constante. Use o corpo exatamente como chegou, antes de converter para JSON, e recuse eventos com timestamp de mais de 5 minutos.
import crypto from "node:crypto";
// corpoBruto: o corpo exatamente como chegou (texto), antes do JSON.parse.
export function assinaturaValida(corpoBruto, cabecalhos, segredo) {
const ts = cabecalhos["x-assinex-timestamp"];
const recebido = String(cabecalhos["x-assinex-signature"] || "").replace("sha256=", "");
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const esperado = crypto.createHmac("sha256", segredo).update(`${ts}.${corpoBruto}`).digest("hex");
return recebido.length === esperado.length &&
crypto.timingSafeEqual(Buffer.from(recebido), Buffer.from(esperado));
}<?php
$corpo = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_ASSINEX_TIMESTAMP'] ?? '';
$recebido = str_replace('sha256=', '', $_SERVER['HTTP_X_ASSINEX_SIGNATURE'] ?? '');
$esperado = hash_hmac('sha256', $ts . '.' . $corpo, getenv('ASSINEX_WEBHOOK_SECRET'));
if (abs(time() - (int) $ts) > 300 || !hash_equals($esperado, $recebido)) {
http_response_code(400);
exit;
}
$evento = json_decode($corpo, true);
http_response_code(200);import hmac, hashlib, time
def assinatura_valida(corpo_bruto: bytes, cabecalhos, segredo: str) -> bool:
ts = cabecalhos.get("X-Assinex-Timestamp", "")
recebido = cabecalhos.get("X-Assinex-Signature", "").replace("sha256=", "")
if abs(time.time() - int(ts or 0)) > 300:
return False
esperado = hmac.new(segredo.encode(), f"{ts}.".encode() + corpo_bruto, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, recebido)Ferramentas
#Playground
Envie requisições reais à API sem sair da página. Escolha o endpoint, informe o seu token e veja a resposta, o status e o tempo.
Ambiente real. Não existe sandbox: as chamadas usam a sua conta. Consultas não alteram nada; POST /orders compra de verdade e só é enviado depois da sua confirmação.
Envie uma requisição para ver a resposta aqui.
O playground precisa de JavaScript. Use os exemplos em cURL de cada endpoint.
#Exemplo de integração
Uma função completa para vender um produto: cria o pedido com o número da venda como external_id, repete com segurança em falhas de rede, 429 e 5xx, e devolve a entrega quando ela vem na resposta. Se o pedido ficar processando, a entrega chega pelo webhook pedido.entregue.
// Rode no servidor. Node.js 18+ (fetch nativo), Bun ou Deno.
const API = "https://assinex.com/api/v1";
const esperar = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function venderProduto(numeroDaVenda, productId, quantidade = 1) {
const corpo = JSON.stringify({
product_id: productId,
quantity: quantidade,
external_id: `venda-${numeroDaVenda}`, // o mesmo em toda tentativa
});
for (let tentativa = 1; tentativa <= 4; tentativa++) {
let resposta;
try {
resposta = await fetch(`${API}/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ASSINEX_TOKEN}`,
"Content-Type": "application/json",
},
body: corpo,
signal: AbortSignal.timeout(30_000),
});
} catch {
await esperar(2_000 * tentativa); // rede ou timeout: repete igual
continue;
}
if (resposta.status === 429 || resposta.status >= 500) {
const segundos = Number(resposta.headers.get("Retry-After")) || 2 * tentativa;
await esperar(segundos * 1_000);
continue;
}
const dados = await resposta.json();
if (!resposta.ok) {
// 4xx: nada foi cobrado. Ex.: saldo_insuficiente, produto_indisponivel.
throw new Error(`${dados.error.code}: ${dados.error.message}`);
}
const { order } = dados;
if (order.status === "entregue") return order.delivery; // mostre delivery.items ao cliente
if (order.status === "processando") return null; // chega no webhook pedido.entregue
throw new Error(`Pedido ${order.id} falhou: ${order.error?.message}`);
}
throw new Error("Assinex indisponível agora. Tente depois com o mesmo número de venda.");
}<?php
// Requer a extensão curl.
function venderProduto(string $numeroDaVenda, string $productId, int $quantidade = 1): ?array
{
$corpo = json_encode([
'product_id' => $productId,
'quantity' => $quantidade,
'external_id' => 'venda-' . $numeroDaVenda, // o mesmo em toda tentativa
]);
for ($tentativa = 1; $tentativa <= 4; $tentativa++) {
$ch = curl_init('https://assinex.com/api/v1/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $corpo,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ASSINEX_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30,
]);
$resposta = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($resposta === false || $status === 429 || $status >= 500) {
sleep(2 * $tentativa); // repete igual: não há cobrança dupla
continue;
}
$dados = json_decode($resposta, true);
if ($status >= 400) {
// 4xx: nada foi cobrado. Ex.: saldo_insuficiente, produto_indisponivel.
throw new RuntimeException($dados['error']['code'] . ': ' . $dados['error']['message']);
}
$pedido = $dados['order'];
if ($pedido['status'] === 'entregue') {
return $pedido['delivery']; // mostre delivery.items ao cliente
}
if ($pedido['status'] === 'processando') {
return null; // chega no webhook pedido.entregue
}
throw new RuntimeException('Pedido ' . $pedido['id'] . ' falhou: ' . ($pedido['error']['message'] ?? ''));
}
throw new RuntimeException('Assinex indisponível agora. Tente depois com o mesmo número de venda.');
}# pip install requests
import os
import time
import requests
API = "https://assinex.com/api/v1"
def vender_produto(numero_da_venda: str, product_id: str, quantidade: int = 1):
corpo = {
"product_id": product_id,
"quantity": quantidade,
"external_id": f"venda-{numero_da_venda}", # o mesmo em toda tentativa
}
cabecalhos = {"Authorization": f"Bearer {os.environ['ASSINEX_TOKEN']}"}
for tentativa in range(1, 5):
try:
resposta = requests.post(f"{API}/orders", json=corpo, headers=cabecalhos, timeout=30)
except requests.RequestException:
time.sleep(2 * tentativa) # rede ou timeout: repete igual
continue
if resposta.status_code == 429 or resposta.status_code >= 500:
time.sleep(int(resposta.headers.get("Retry-After", 2 * tentativa)))
continue
dados = resposta.json()
if not resposta.ok:
# 4xx: nada foi cobrado. Ex.: saldo_insuficiente, produto_indisponivel.
erro = dados["error"]
raise RuntimeError(f"{erro['code']}: {erro['message']}")
pedido = dados["order"]
if pedido["status"] == "entregue":
return pedido["delivery"] # mostre delivery["items"] ao cliente
if pedido["status"] == "processando":
return None # chega no webhook pedido.entregue
raise RuntimeError(f"Pedido {pedido['id']} falhou: {(pedido.get('error') or {}).get('message')}")
raise RuntimeError("Assinex indisponível agora. Tente depois com o mesmo número de venda.")#Boas práticas
- Use o número da venda da sua loja como
external_id. Em qualquer dúvida (timeout, erro 500), repita a mesma requisição. - Guarde o token e o segredo do webhook em variáveis de ambiente do servidor. Nunca no site, no app ou no Git.
- Crie um token por sistema. Se um vazar, revogue só ele.
- Verifique a assinatura de todo webhook e ignore eventos repetidos pelo
id. - Confira
GET /balanceantes de campanhas grandes e mantenha saldo para as vendas do dia. - Trate
produto_indisponivelcomo temporário: avise o seu cliente e tente mais tarde. - Decida pelo
error.code, nunca pelo texto deerror.message.
Precisa de ajuda com a integração? Entre no painel, onde ficam os seus pedidos, os eventos de webhook, os tokens e o suporte.