Contrato da API V1
Recomendamos a API server-to-server. O JavaScript é apenas uma via analítica de recurso e funciona apenas depois de ser dado o consentimento para análise.
Contrato da API V1 Ligação para a secção Contrato da API V1
Recomendamos a API server-to-server. O JavaScript é apenas uma via analítica de recurso e funciona apenas depois de ser dado o consentimento para análise.
As encomendas, a receita e as métricas derivadas só são apresentadas quando a medição de conversões está ativa. Servem apenas para análise e não alteram a faturação CPC.
schema_version
1.0
payload_contract
order_v1
Content-Type
application/json
request_limit
64 KiB
Como ligar a medição Ligação para a secção Como ligar a medição
Recomendamos a API server-to-server. O JavaScript é apenas uma via analítica de recurso e funciona apenas depois de ser dado o consentimento para análise.
- 1 Guarde o parâmetro zclid do URL de destino no carrinho ou na encomenda durante 30 dias.
- 2 No servidor, crie uma impressão digital HMAC-SHA-256 estável do ID interno da encomenda, utilizando uma chave separada. Não envie o ID bruto nem dados pessoais.
- 3 Depois de criar a encomenda, envie o JSON para a API e assine o corpo exato do pedido com a chave secreta de integração.
- 4 Para pagamento, cancelamento e reembolsos acumulados, reutilize os mesmos zclid e order_id_hash. Não altere os totais finais nem as linhas.
A chave secreta de integração só será apresentada uma vez. Guarde-a no gestor de segredos do servidor da loja.
Recomendado: API server-to-server Ligação para a secção Recomendado: API server-to-server
O servidor da loja envia encomendas verificadas, alterações de estado e reembolsos diretamente para a Zoneo. Nunca coloque a chave secreta no navegador.
https://zoneo.pt/api/v1/conversions
https://zoneo.pt/api/v1/conversions/sandbox
No servidor, crie uma impressão digital HMAC-SHA-256 estável do ID interno da encomenda, utilizando uma chave separada. Não envie o ID bruto nem dados pessoais.
order_id_hash · PHP
$orderIdHash = hash_hmac(
'sha256',
"zoneo-order-v1\n".$internalOrderId,
$_ENV['ZONEO_ORDER_HASH_KEY'],
);
Exemplo de pedido Ligação para a secção Exemplo de pedido
Depois de criar a encomenda, envie o JSON para a API e assine o corpo exato do pedido com a chave secreta de integração.
order_v1 · JSON
{
"schema_version": "1.0",
"zclid": "018fb72a-7d8e-7c3c-a4da-f37ce07ad739",
"order_id_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"currency": "EUR",
"occurred_at": "2026-08-31T12:34:56Z",
"status": "placed",
"refund_amount_minor": 0,
"totals": {
"items_gross_minor": 14000,
"discount_minor": 1500,
"shipping_gross_minor": 390,
"fees_gross_minor": 100,
"tax_minor": 2165,
"order_total_gross_minor": 12990
},
"items": [
{
"merchant_item_id": "ITEM_ID_FROM_FEED",
"item_group_id": "MODEL-10",
"variant_id": "size:42",
"name": "PRODUCT_NAME",
"gtin": "8581234567890",
"quantity": 2,
"unit_price_gross_minor": 7000,
"line_total_gross_minor": 14000
}
],
"order_locale": "pt-pt",
"expected_delivery_date": "2026-09-03"
}
| JSON | Campos obrigatórios | V1 |
|---|---|---|
schema_version |
✓ | = "1.0" |
zclid |
✓ | UUID |
order_id_hash |
✓ | HMAC-SHA-256 · [a-f0-9]{64} |
currency |
✓ | ISO 4217 · EUR |
occurred_at |
✓ | ISO 8601 · UTC |
status |
✓ | placed | paid | cancelled | partially_refunded | refunded |
refund_amount_minor |
✓ | integer ≥ 0 · Σ · monotonic |
totals |
✓ | object · integer · gross |
items |
✓ | array[1..100] |
order_locale |
— | BCP 47 |
expected_delivery_date |
— | YYYY-MM-DD |
| items[] | Campos obrigatórios | V1 |
|---|---|---|
merchant_item_id |
✓ | feed.ITEM_ID · stable |
quantity |
✓ | integer · 1..1000 |
unit_price_gross_minor |
✓ | integer ≥ 0 |
line_total_gross_minor |
✓ | unit_price_gross_minor × quantity |
item_group_id |
— | string |
variant_id |
— | string |
name |
— | string · PRODUCT_NAME · PII = 0 |
gtin |
— | [0-9]{8,14} |
totals · EUR · integer
totals.items_gross_minor = sum(items[].line_total_gross_minor)
totals.order_total_gross_minor = totals.items_gross_minor - totals.discount_minor + totals.shipping_gross_minor + totals.fees_gross_minor
line_total_gross_minor = unit_price_gross_minor × quantity
Assinatura canónica Ligação para a secção Assinatura canónica
Se não tiver guardado a chave secreta original, utilize Recuperar chave secreta e guarde imediatamente a nova chave de forma segura.
| HTTP | V1 |
|---|---|
Content-Type |
application/json |
X-Zoneo-Integration-ID |
zci_... |
X-Zoneo-Timestamp |
Unix · UTC |
X-Zoneo-Nonce |
CSPRNG · unique · len ≥ 16 |
Idempotency-Key |
order:{hash}:{status} |
X-Zoneo-Signature |
v1=HMAC_SHA256_HEX |
HMAC-SHA-256 · canonical request
UPPERCASE_HTTP_METHOD
/exact/request/path
unix_timestamp
nonce
idempotency_key
sha256_hex_of_exact_raw_body
body_hash = SHA256(raw_body)
signature = HMAC_SHA256(api_secret, canonical_request)
X-Zoneo-Signature = "v1=" + lowercase_hex(signature)
S2S · PHP
<?php
$path = '/api/v1/conversions';
$body = json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
$idempotencyKey = 'order:'.$orderIdHash.':'.$payload['status'];
$canonical = implode("\n", [
'POST',
$path,
(string) $timestamp,
$nonce,
$idempotencyKey,
hash('sha256', $body),
]);
$signature = hash_hmac('sha256', $canonical, $_ENV['ZONEO_API_SECRET']);
$headers = [
'Content-Type: application/json',
'X-Zoneo-Integration-ID: '.$_ENV['ZONEO_INTEGRATION_ID'],
'X-Zoneo-Timestamp: '.$timestamp,
'X-Zoneo-Nonce: '.$nonce,
'Idempotency-Key: '.$idempotencyKey,
'X-Zoneo-Signature: v1='.$signature,
];
$curl = curl_init('https://zoneo.pt/api/v1/conversions');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
Criada → Reembolsada Ligação para a secção Criada → Reembolsada
Para pagamento, cancelamento e reembolsos acumulados, reutilize os mesmos zclid e order_id_hash. Não altere os totais finais nem as linhas.
order_v1 · lifecycle
placed -> paid | cancelled | partially_refunded | refunded
paid -> partially_refunded | refunded
partially_refunded -> refunded
cancelled, refunded -> terminal
0 <= refund_amount_minor <= totals.order_total_gross_minor
new_refund_amount_minor >= previous_refund_amount_minor
Idempotency-Key · retry
nonce₁ != nonce₂
retry = nonce₂ + Idempotency-Key₁ + SHA256(JSON₁)
Idempotency-Key₁ + SHA256(JSON₁) -> HTTP 200
Idempotency-Key₁ + SHA256(JSON₂) -> HTTP 409 idempotency_conflict
Sandbox V1 Ligação para a secção Sandbox V1
Cole um JSON V1 para validar campos, totais e associação ao feed sem criar uma encomenda nem afetar a faturação.
https://zoneo.pt/api/v1/conversions/sandbox
Medição opcional através de JavaScript Ligação para a secção Medição opcional através de JavaScript
A biblioteca guarda zclid após o consentimento e envia apenas o evento placed inicial na página de agradecimento. Envie os estados seguintes de forma segura por S2S.
O consentimento está desativado por predefinição. A função consent só deve devolver true depois de o utilizador dar um consentimento válido para análise.
Carregamento e inicialização
<script src="https://zoneo.pt/integrations/zoneo-conversion-v1.js"></script>
<script>
const zoneo = window.ZoneoConversions.init({
integrationId: 'zci_...',
apiBase: 'https://zoneo.pt/api/v1/conversions',
consent: () => analyticsConsent === true
})
zoneo.track({
order_id_hash: 'SERVER_HMAC_SHA256',
currency: 'EUR',
occurred_at: new Date().toISOString(),
status: 'placed',
totals: {
items_gross_minor: 12990,
discount_minor: 0,
shipping_gross_minor: 0,
fees_gross_minor: 0,
tax_minor: 2165,
order_total_gross_minor: 12990
},
items: [{
merchant_item_id: 'ITEM_ID_FROM_FEED',
quantity: 1,
unit_price_gross_minor: 12990,
line_total_gross_minor: 12990
}]
})
</script>
Estado da integração Ligação para a secção Estado da integração
Eventos aceites e rejeitados nos últimos 7 dias.
HTTP 201 · JSON
{
"data": {
"conversion_reference": "6bfca33e-3ac7-48dc-a733-c1f313853269",
"status": "placed",
"source": "s2s",
"verification": "hmac_current",
"schema_version": "1.0",
"payload_contract": "order_v1",
"totals": {
"items_gross_minor": 14000,
"discount_minor": 1500,
"shipping_gross_minor": 390,
"fees_gross_minor": 100,
"tax_minor": 2165,
"order_total_gross_minor": 12990
},
"refund_amount_minor": 0,
"net_revenue_minor": 12990,
"items": {
"count": 1,
"quantity_total": 2,
"matched_count": 1,
"match_status": "complete"
},
"totals_reconciled": true,
"warnings": [],
"currency": "EUR",
"created": true,
"idempotent": false,
"deduplicated": false,
"provisional": false,
"billing_impact": false
}
}
HTTP 4xx · JSON
{
"error": {
"code": "order_total_mismatch",
"field": "totals.order_total_gross_minor",
"details": {
"expected_minor": 12990,
"received_minor": 13000
}
}
}
invalid_signature
stale_timestamp
replayed_nonce
pii_not_allowed
items_total_mismatch
order_total_mismatch
currency_mismatch
click_not_eligible
store_or_market_mismatch
not_last_zoneo_click
attribution_window_expired
invalid_state_transition
order_definition_conflict
refund_amount_decreased
order_attribution_conflict
Proteção de dados pessoais Ligação para a secção Proteção de dados pessoais
As encomendas mais recentes recebidas pela Zoneo apenas para análise. Nunca mostramos IDs originais nem dados pessoais.
No servidor, crie uma impressão digital HMAC-SHA-256 estável do ID interno da encomenda, utilizando uma chave separada. Não envie o ID bruto nem dados pessoais.
As encomendas, a receita e as métricas derivadas só são apresentadas quando a medição de conversões está ativa. Servem apenas para análise e não alteram a faturação CPC.
Como ligar a medição
Recomendamos a API server-to-server. O JavaScript é apenas uma via analítica de recurso e funciona apenas depois de ser dado o consentimento para análise.