Começando

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:

PrefixoAmbienteComportamento
sk_test_SandboxGratuito e ilimitado. Respostas determinísticas por fixture (ver abaixo) — ideal para CI e desenvolvimento.
sk_live_ProduçãoProcessa imagens reais e consome unidades.
Nunca exponha chaves no navegador ou no app. Chame a API apenas do seu backend. Para captura no cliente, use o Capture Kit, que entrega a imagem ao seu servidor — não à nossa API.
1

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.

2

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
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.

3

Interprete os scores

resposta · 200
{
  "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 }
}

Testando no sandbox

Com sk_test_, o nome do arquivo controla a resposta — determinístico para CI:

Arquivo enviadoResultado simulado
qualquer.jpgsucceeded com scores de fixture
test_dark.jpgfailed · lighting_too_dark
test_noface.jpgfailed · face_not_found
test_small.jpgfailed · image_too_small