Documentação · Guias
Sessões alojadas, passo a passo
O seu backend cria uma sessão e manda a pessoa para uma página própria do Urdume. Lá ela completa os dados (também a partir de documentos, com IA), emite o passaporte, leva o QR Code e volta ao seu sistema. Sem conta Urdume para cada pessoa e sem guardar nada do Urdume na sua base de dados.
Como funciona
- Na ficha do produto do seu painel, a pessoa carrega em "Passaporte digital".
- O seu servidor cria a sessão (POST /passport_sessions) com a referência do produto e quem vai preencher, e redireciona para "url".
- Na página do Urdume a pessoa completa os dados, revê, confirma e emite. Descarrega o QR Code (SVG ou PNG) e, se pediu números de série, a lista dos artigos.
- Ao carregar em "Concluir e voltar" regressa ao seu success_url, com o ID da sessão.
- O seu servidor confirma a sessão pela API (ou recebe o webhook) e o passaporte fica sempre em /r/{empresa}/{referência}.
1. Criar a sessão no seu servidor
"operator" identifica a pessoa que vai preencher (já autenticada no seu sistema): fica no registo de atividade e nas versões que emitir. Se a referência ainda não existir no Urdume, o produto é criado (aí "name" é obrigatório e "data" pré-preenche os campos).
POST /passport_sessions
curl
curl https://urdume.site/api/v1/passport_sessions \
-H "Authorization: Bearer $URDUME_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dpp-produto-100-ana-1" \
-d '{
"reference": "TS-100",
"name": "T-shirt básica",
"operator": {"name": "Ana Silva", "email": "ana@loja.exemplo.pt"},
"success_url": "https://loja.exemplo.pt/admin/produtos/100?dpp={SESSION_ID}",
"cancel_url": "https://loja.exemplo.pt/admin/produtos/100",
"metadata": {"produto_id": "100"}
}'
PHP
<?php
// No seu backend, quando a pessoa carrega em "Passaporte digital" na ficha do produto.
$payload = [
'reference' => $produto['referencia'],
'name' => $produto['nome'],
'operator' => ['name' => $utilizador['nome'], 'email' => $utilizador['email']],
'success_url' => 'https://loja.exemplo.pt/admin/produtos/' . $produto['id'] . '?dpp={SESSION_ID}',
'cancel_url' => 'https://loja.exemplo.pt/admin/produtos/' . $produto['id'],
'metadata' => ['produto_id' => (string) $produto['id']],
];
$ch = curl_init('https://urdume.site/api/v1/passport_sessions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('URDUME_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$session = json_decode((string) curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException($session['error']['message'] ?? 'Erro ao contactar o Urdume');
}
header('Location: ' . $session['url'], true, 303);
exit;
Node
// Express + Node 18+
app.post('/admin/produtos/:id/passaporte', async (req, res) => {
const produto = await produtos.find(req.params.id);
const response = await fetch('https://urdume.site/api/v1/passport_sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.URDUME_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
reference: produto.referencia,
name: produto.nome,
operator: { name: req.user.name, email: req.user.email },
success_url: `https://loja.exemplo.pt/admin/produtos/${produto.id}?dpp={SESSION_ID}`,
cancel_url: `https://loja.exemplo.pt/admin/produtos/${produto.id}`,
metadata: { produto_id: String(produto.id) },
}),
});
const session = await response.json();
if (!response.ok) throw new Error(session.error.message);
res.redirect(303, session.url);
});
Python
# Flask + requests
import os, requests
from flask import redirect
@app.post("/admin/produtos/<int:produto_id>/passaporte")
def criar_passaporte(produto_id):
produto = produtos.find(produto_id)
response = requests.post(
"https://urdume.site/api/v1/passport_sessions",
headers={"Authorization": f"Bearer {os.environ['URDUME_KEY']}"},
json={
"reference": produto.referencia,
"name": produto.nome,
"operator": {"name": current_user.name, "email": current_user.email},
"success_url": f"https://loja.exemplo.pt/admin/produtos/{produto_id}?dpp={{SESSION_ID}}",
"cancel_url": f"https://loja.exemplo.pt/admin/produtos/{produto_id}",
"metadata": {"produto_id": str(produto_id)},
},
timeout=15,
)
session = response.json()
response.raise_for_status()
return redirect(session["url"], code=303)
| Parâmetro | Descrição |
|---|---|
reference |
Obrigatório. A referência do produto no seu sistema. |
operator[name] |
Obrigatório. Quem vai preencher (até 160 caracteres). operator[email] é opcional. |
success_url |
Obrigatório. HTTPS (http://localhost só em testes). {SESSION_ID} é trocado pelo ID da sessão. |
cancel_url |
Opcional. Para onde leva o link "Voltar" sem concluir. |
serials |
Opcional. Números de série das peças (até 5000): os que faltarem são criados ao emitir. |
metadata |
Opcional. Até 20 pares chave/valor, devolvidos na sessão e no webhook (ex.: o ID do produto na loja). |
expires_at |
Opcional. Data Unix entre 30 minutos e 24 horas a partir de agora (por omissão, 24 horas). |
2. O que a pessoa vê
- À esquerda, a sua marca (logótipo, cores e letras de Definições › Marca) e o produto; à direita, os passos.
- Dados: o formulário do modelo têxtil, com o que já existir pré-preenchido. Pode carregar a ficha técnica e certificados: a IA sugere valores e a pessoa aceita, corrige ou rejeita cada um.
- Rever e emitir: um resumo e a confirmação "declaro em nome de…". Emite o passaporte, ou uma nova versão se os dados mudaram.
- Concluído: o QR Code (SVG e PNG), o endereço público, o endereço pela referência, o conteúdo NFC e a lista dos artigos (CSV).
3. No regresso, confirme pela API
Não confie só no endereço de regresso: confirme o estado com GET /passport_sessions/{id}. "outcome" diz o que aconteceu: issued (passaporte novo), version (nova versão) ou unchanged (os dados já estavam publicados).
GET /passport_sessions/{id}
curl
curl https://urdume.site/api/v1/passport_sessions/01JA2… \
-H "Authorization: Bearer $URDUME_KEY"
{
"id": "01JA2…",
"object": "passport_session",
"status": "completed",
"outcome": "issued",
"reference": "TS-100",
"public_url": "https://urdume.site/r/a-sua-marca/TS-100",
"passport": "01JA3…",
"passport_url": "https://urdume.site/p/01JA3…",
"items_created": 0,
"operator": {"name": "Ana Silva", "email": "ana@loja.exemplo.pt"},
"metadata": {"produto_id": "100"}
}
PHP
<?php
// success_url: /admin/produtos/100?dpp=01JA2… (confirme sempre pela API, não pelo endereço)
$id = $_GET['dpp'] ?? '';
if (!preg_match('/^[0-9A-Z]{26}$/', $id)) {
exit('Sessão inválida');
}
$ch = curl_init('https://urdume.site/api/v1/passport_sessions/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('URDUME_KEY')],
]);
$session = json_decode((string) curl_exec($ch), true);
if (($session['status'] ?? '') === 'completed') {
// Guarde (se quiser) o endereço público; a referência já chega para o voltar a montar.
guardarPassaporte($session['metadata']['produto_id'], $session['public_url']);
}
4. Ou receba o webhook
A pessoa pode fechar o separador sem voltar. Para não depender disso, subscreva o evento passport_session.completed em Programadores › Webhooks: chega logo que o passaporte é emitido, com a sessão completa (incluindo "metadata"). Veja o guia de webhooks para verificar a assinatura.
5. Mostre o passaporte na loja
O endereço pela referência não muda nunca: https://urdume.site/r/a-sua-marca/TS-100 (e /TS-100/{série} para uma peça). Use-o em links, no QR Code e no NFC, ou mostre um resumo na página do produto com o widget.
Erros frequentes
| Código | O que fazer |
|---|---|
parameter_missing (operator.name) |
Indique quem vai preencher a página. |
invalid_return_url |
success_url/cancel_url têm de ser absolutos e HTTPS (http://localhost só em testes). |
expires_at_out_of_range |
Use uma data entre 30 minutos e 24 horas a partir de agora. |
product_archived |
O produto com esta referência está arquivado no Urdume: reative-o no painel. |
plan_limit_reached |
O plano não permite mais produtos: reveja o plano ou use dados de teste. |
missing_api_key / invalid_api_key (401) |
Chave em falta, errada ou revogada. |
api_not_in_plan (403) |
Chave sk_live_ num plano sem API em produção: use sk_test_ ou reveja o plano. |