PopNowDocs da API
Construindo com a API
Conteúdo principal

Idempotência

Tente de novo depois de um timeout sem gerar duas vezes nem pagar duas vezes.

Uma chave de idempotência é uma etiqueta que você coloca num trabalho, como "renderizar o vídeo do pedido 8814". Se a mesma requisição etiquetada chegar duas vezes à PopNow, o trabalho é feito uma vez só.

POST /api/v1/jobs/createTask
Idempotency-Key: order-8814-render-1

Por que usar

Um timeout deixa você no escuro: talvez a requisição nunca tenha chegado, talvez tenha chegado e só a resposta se perdeu. Sem chave, ou você desiste (e o usuário espera à toa) ou tenta de novo (e talvez gere e pague duas vezes). Com chave, tentar de novo é sempre seguro: você recebe uma tarefa nova ou a que acabou de ser criada.

Como funciona

  • Repetir com a mesma chave, a partir da mesma chave de API, devolve a tarefa original no estado atual — nada de segunda tarefa, segunda reserva ou segunda cobrança.
  • A chave fica presa ao que você enviou: model, o input inteiro e callBackUrl. Ordem dos campos e espaços não importam, mas qualquer mudança real importa. Uma requisição alterada com uma chave antiga retorna 409; dê uma chave nova a ela.
  • Uma chave é lembrada por 24 horas a partir do primeiro uso. Depois disso, a mesma chave cria uma tarefa nova e cobrada, então pare as tentativas automáticas bem antes.
  • As chaves têm até 128 caracteres e só precisam ser únicas na sua conta. Outra chave de API da sua conta não pode reaproveitá-la: recebe 409 sem o ID da tarefa original.
EndpointRepetir com a mesma chave…
POST /api/v1/jobs/createTaskdevolve a tarefa original
POST /api/v1/jobs/retrydevolve a tarefa que aquele retry criou (a chave vale por tarefa de origem)
POST /v1/videosdevolve a tarefa original
POST /api/v1/assetsdevolve o asset já salvo (aqui o cabeçalho é obrigatório)

Endpoints de leitura e os que geram links de upload ou download ignoram o cabeçalho. jobs/purge não precisa: repeti-lo já é inofensivo.

Escolhendo chaves

  • Uma chave por trabalho, não por tentativa HTTP. Toda nova tentativa precisa reutilizá-la.
  • Derive de algo que você já guarda: order-8814-render-1 pode ser recalculada depois de uma queda; um UUID aleatório, não — a não ser que você o salve antes.
  • Se usar UUID, gere antes da primeira tentativa e guarde junto com o trabalho a que ele pertence.

Nunca gere a chave dentro do loop de novas tentativas

// Wrong: a new key per attempt defeats the whole mechanism
for (let i = 0; i < 3; i++) await createTask(body, crypto.randomUUID());

// Right: one key, created once
const key = crypto.randomUUID();
for (let i = 0; i < 3; i++) await createTask(body, key);

Conflitos

Todos estes casos retornam code: 409:

O que aconteceuTentar de novo?
Duas requisições com a mesma chave chegaram juntasSim — espere algumas centenas de milissegundos e reenvie com a mesma chave
A chave já foi usada em outra requisiçãoNão — a requisição mudou e precisa de chave nova
Outra chave de API da sua conta já usou esta chaveNão — tente com a chave de API que fez a primeira tentativa

Seu código não deve decidir pelo msg, então permita uma ou duas novas tentativas em 409 e depois mostre o erro para uma pessoa.

Exemplo completo

JavaScript
const RETRYABLE = new Set([429, 500, 503, 50301]); // business codes from the body

async function createTaskOnce(body, idempotencyKey) {
  for (let attempt = 0; attempt < 4; attempt++) {
    try {
      const res = await fetch('https://api.popnow.ai/api/v1/jobs/createTask', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.POPNOW_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify(body),
      });
      const json = await res.json();
      if (json.code === 200) return json.data; // a new task, or the original one
      if (!RETRYABLE.has(json.code)) throw new Error(`${json.code} ${json.msg} (${json.request_id})`);
    } catch (err) {
      if (!(err instanceof TypeError)) throw err; // network error: safe to retry with the same key
    }
    await new Promise((r) => setTimeout(r, 2 ** attempt * 500 + Math.random() * 300));
  }
  throw new Error('createTask did not succeed after 4 attempts');
}

const task = await createTaskOnce(
  { model: 'alibaba/z-image-turbo/text-to-image', input: { prompt: 'A paper boat on a calm lake' } },
  'order-8814-render-1',
);

Nesta página