Guia de integração
Da imagem ao resultado em três passos. Diferente do padrão do mercado — que exige registrar o arquivo, receber uma URL pré-assinada, fazer o upload e só então criar a tarefa — aqui a análise começa com uma única chamada.
Autenticação e chaves
Toda chamada leva Authorization: Bearer <chave>. Existem dois tipos de chave:
| Prefixo | Ambiente | Comportamento |
|---|---|---|
sk_test_ | Sandbox | Gratuito e ilimitado. Respostas determinísticas por fixture (ver abaixo) — ideal para CI e desenvolvimento. |
sk_live_ | Produção | Processa imagens reais e consome unidades. |
Crie a análise
Envie a imagem diretamente (multipart) com as dimensões desejadas e o consentimento do titular. Use Idempotency-Key para que retries de rede jamais dupliquem análise ou cobrança.
curl -X POST https://api.nutoken.com.br/v1/analyses \
-H "Authorization: Bearer sk_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-F "image=@selfie.jpg" \
-F 'dimensions=["wrinkle","pore","texture","acne","oiliness","moisture","redness","age_spot"]' \
-F 'consent={"obtained":true,"policy_version":"2026-10","subject_ref":"u_84f2"}' \
-F "webhook_url=https://seuapp.com/hooks/aestheticos"const form = new FormData();
form.append('image', fs.createReadStream('selfie.jpg'));
form.append('dimensions', JSON.stringify(['wrinkle','pore','texture','acne',
'oiliness','moisture','redness','age_spot']));
form.append('consent', JSON.stringify({ obtained: true,
policy_version: '2026-10', subject_ref: 'u_84f2' }));
const r = await fetch('https://api.nutoken.com.br/v1/analyses', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.AOS_KEY}`,
'Idempotency-Key': crypto.randomUUID() },
body: form,
});
const { analysis_id } = await r.json();import requests, uuid, json
r = requests.post(
"https://api.nutoken.com.br/v1/analyses",
headers={"Authorization": f"Bearer {AOS_KEY}",
"Idempotency-Key": str(uuid.uuid4())},
files={"image": open("selfie.jpg", "rb")},
data={"dimensions": json.dumps(["wrinkle","pore","texture","acne",
"oiliness","moisture","redness","age_spot"]),
"consent": json.dumps({"obtained": True,
"policy_version": "2026-10",
"subject_ref": "u_84f2"})},
)
analysis_id = r.json()["analysis_id"]A resposta é 202 Accepted com o analysis_id. Imagens acima de 10 MB ou fluxos em lote podem usar o fluxo opcional de upload pré-assinado (referência, POST /v1/uploads) — mas para o caso comum você não precisa dele.
Receba o resultado
Preferido — webhook: informe webhook_url (ou configure um endpoint padrão no painel) e receba analysis.completed assinado com HMAC em segundos. Sem polling, sem desperdício. Detalhes em Webhooks.
Fallback — polling:
curl https://api.nutoken.com.br/v1/analyses/ana_7Kp2mQ \ -H "Authorization: Bearer sk_live_..."
Faça polling com intervalo ≥ 2 s e backoff. O resultado fica disponível por 72 horas (vs. 24 h do padrão de mercado); depois disso, apenas os metadados permanecem.
Interprete os scores
{
"analysis_id": "ana_7Kp2mQ",
"status": "succeeded",
"model_version": "skin-v1.4.2",
"overall": { "score": 71, "confidence": 0.92 },
"skin_age": { "years": 34, "chronological_delta": "+2" },
"dimensions": [
{ "dimension": "texture", "score": 71, "confidence": 0.94,
"display_score": 74,
"regions": [ { "area": "cheek_left", "severity": "mild",
"mask_url": "https://cdn....png?sig=..." } ] }
],
"safety": { "flags": [], "disclaimer": "informative_non_diagnostic" },
"units": { "consumed": 1, "remaining": 499 }
}score(0–100, maior = pele com menos sinais da condição) é o valor técnico, estável entre versões de modelo para a mesma foto.display_scoreé a variante calibrada para exibição ao consumidor — e a calibração é documentada, não uma caixa-preta.confidenceabaixo de 0,70 merece tratamento de "resultado parcial" na sua UI.- Máscaras chegam como URLs assinadas por região (72 h) — sem ZIP para baixar e descompactar.
Testando no sandbox
Com sk_test_, o nome do arquivo controla a resposta — determinístico para CI:
| Arquivo enviado | Resultado simulado |
|---|---|
qualquer.jpg | succeeded com scores de fixture |
test_dark.jpg | failed · lighting_too_dark |
test_noface.jpg | failed · face_not_found |
test_small.jpg | failed · image_too_small |