Esta página foi traduzida do inglês. A versão em inglês é a fonte da verdade — se algo parecer estranho, confira por lá. EN

Chaves de API e acesso programático

Crie formulários, clone-os e gere links de admissão de clientes a partir do seu próprio backend usando chaves de API com escopo de workspace.

Se você já opera um CRM, um sistema de gestão de casos ou um portal interno, não precisa entrar no painel para criar cada formulário. As chaves de API permitem que o seu backend chame a API v1 da DS160.io diretamente: criar formulários, cloná-los e gerar links de admissão de clientes no seu próprio horário.

As chaves de API estão disponíveis em workspaces do tipo Business. Cada chave está vinculada a um único workspace e só pode operar dentro dele.

URL base da API

Todos os endpoints v1 têm como raiz:

https://ds160.io/api/v1

Estes documentos podem ser hospedados no domínio white-label da sua agência, mas as chamadas à API sempre vão para ds160.io. Todos os exemplos de código nesta página usam a URL completa para que você possa copiar e colar sem reescrever.

Referência de endpoints

/v1 é estável — as rotas e os nomes dos campos não mudarão sem uma versão maior /v2 (ver Versionamento e suporte). As rotas da tabela abaixo são mostradas relativas ao prefixo de workspace /workspaces/:workspaceId/; as estruturas completas das solicitações e respostas estão documentadas na seção de cada endpoint.

MétodoRotaScopeCorpo da solicitação
POST/formsforms:writename?
POST/forms/:formId/cloneforms:clonedisabledSections?
POST/forms/:formId/client-linksclient-links:writeexpiresInDays, defaultLanguage, hideBranding?, lenientPhotoUpload?
GET/formsforms:read / forms:writequery: limit?, cursor?
GET/forms/:formIdforms:read / forms:write

Todos os corpos e respostas são JSON. O scope forms:read concede acesso somente de leitura aos metadados de formulários; forms:write concede leitura e escrita, então uma chave que apenas escreve também tem leitura de graça.

1. Criar uma chave de API

Abra Configurações do Workspace → Equipe e acesso → Chaves de API e clique em Criar chave de API. Escolha um nome descritivo (recomendamos uma chave por integração, p. ex. Production CRM ou Staging webhook handler) e selecione os scopes que a integração precisa:

  • forms:read — ler metadados de formulários (listar / obter)
  • forms:write — criar formulários; também concede leitura
  • forms:clone — duplicar um formulário existente
  • client-links:write — gerar links de admissão de clientes

Apenas os Proprietários e Administradores do workspace podem gerar chaves.

Quando você clica em Criar chave, a plataforma exibe o segredo completo uma única vez. Copie-o imediatamente para o seu gerenciador de segredos: após fechar o modal, somente os últimos quatro caracteres serão exibidos novamente. Se você perder uma chave, revogue-a e gere uma nova.

2. Autenticar

Todos os endpoints v1 esperam um cabeçalho Authorization: Bearer <secret>.

export DS160_KEY="...the secret you just copied..."
export DS160_WORKSPACE="your-workspace-id"

curl https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms \
  -H "Authorization: Bearer $DS160_KEY"

O id do workspace aparece na URL de cada página do painel (/workspaces/<workspaceId>/...). O workspace da chave está vinculado no lado do servidor: chamar o endpoint de outro workspace com a chave errada retorna 403 Forbidden.

Dica: as abas de linguagem se sincronizam entre todos os trechos desta página. Escolha a sua linguagem uma vez e o resto da página acompanha.

3. Criar um formulário

curl -X POST https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms \
  -H "Authorization: Bearer $DS160_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Smith / B1 — 2026-05" }'
# → { "formId": "65f2…" }

Cada chamada consome um crédito de formulário do seu plano de faturamento (igual a criar um formulário pelo painel). Se o seu workspace ficar sem créditos, a chamada retorna 402 Payment Required: recarregue antes de tentar novamente.

O campo opcional name define um rótulo legível para o formulário (até 200 caracteres). Omita-o para obter um nome gerado automaticamente — de qualquer forma, você pode renomear o formulário pelo painel mais tarde.

4. Clonar um formulário existente

Se você tem um formulário modelo que reemite com frequência (digamos, o mesmo programa J-1 do mesmo empregador), clone-o em vez de começar do zero. Opcionalmente, passe disabledSections para omitir páginas específicas do formulário na cópia — qualquer item que você listar é substituído por campos vazios no novo formulário, de modo que o cliente o preencha do zero:

curl -X POST https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms/$SOURCE_FORM_ID/clone \
  -H "Authorization: Bearer $DS160_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "disabledSections": ["spouse-info-page", "security-background-page-5"] }'
# → { "formId": "65f3…" }

Clonar consome um crédito de formulário, assim como criar um novo.

Valores válidos de disabledSections

Passe um array vazio (ou omita o campo inteiramente) para copiar todas as páginas. Caso contrário, use qualquer combinação dos identificadores abaixo — qualquer outro valor retorna 400 Bad Request.

IdentificadorPágina
personal-info-page-1Personal Information - Part 1
personal-info-page-2Personal Information - Part 2
visa-purpose-pagePurpose of Visa
travel-companions-pageTravel Companions
previous-us-travel-pagePrevious U.S. Travel History
address-and-phone-pageAddress and Phone Details
passport-pagePassport Information
contact-info-pageContact Information
family-info-pageFamily Information
spouse-info-pageSpouse Information
deceased-spouse-info-pageDeceased Spouse Information
former-spouse-info-pageFormer Spouse Information
present-occupation-pageCurrent Occupation
previous-occuptation-pagePrevious Occupation
additional-occuptation-pageAdditional Occupation Details
security-background-page-1Security Background - Part 1
security-background-page-2Security Background - Part 2
security-background-page-3Security Background - Part 3
security-background-page-4Security Background - Part 4
security-background-page-5Security Background - Part 5
student-visa-page-1Student Visa Details - Part 1
student-visa-page-2Student Visa Details - Part 2
temporary-visa-pageTemporary Visa Information
crew-visa-pageCrew Visa Information

Apenas as seções relevantes para a categoria de visto do formulário estão realmente presentes; listar uma página que não existe no formulário de origem não tem efeito.

Uma vez que um formulário exista, gere uma URL com token que o cliente pode usar para preenchê-lo:

curl -X POST https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms/$FORM_ID/client-links \
  -H "Authorization: Bearer $DS160_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "expiresInDays": 7, "defaultLanguage": "en" }'

Corpo da solicitação

CampoObrigatórioNotas
expiresInDayssimInteiro 1–365. Sem valor padrão — omiti-lo retorna 400 Bad Request. O expiresAt do link é calculado como now + expiresInDays e o registro de token subjacente é excluído automaticamente nesse momento.
defaultLanguagesimUm dos códigos em Valores válidos de defaultLanguage. Define o prefixo de localização na url retornada e o idioma em que o formulário de admissão é aberto.
hideBrandingnãoBooleano. Quando true, o formulário de admissão é exibido sem marca — o logo e o tema white-label da agência são suprimidos para este link. O padrão é false (a identidade white-label do workspace é exibida se estiver configurada).
lenientPhotoUploadnãoBooleano. Quando true, o campo da foto da solicitação aceita qualquer foto (JPEG/PNG/PDF de até 30 MB, como a digitalização do passaporte) e as verificações estritas da foto do DS-160 tornam-se avisos não bloqueantes em vez de rejeitar o upload. Omita-o para herdar a configuração de agência Permitir qualquer upload de foto do workspace; passe um true/false explícito para sobrescrever esse padrão neste link. Consulte Armazenamento de Documentos e Automação de Fotos.

Exemplo de resposta (200 OK)

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2NmMxYmQ0ZjZmNGFkNTQwMzMxNDhmYmEiLCJmb3JtSWQiOiI2NWYyYTkxMTNkZjAxYzAwMTI3YjY4MmEiLCJ3b3Jrc3BhY2VJZCI6IjY1ZjJhOTAwM2RmMDFjMDAxMjdiNjgwYSIsImV4cCI6MTc0ODA0ODI0NywiaWF0IjoxNzQ3NDQzNDQ3LCJkZWZhdWx0TGFuZ3VhZ2UiOiJlbiIsImhpZGVCcmFuZGluZyI6ZmFsc2V9.SiGNATuRe",
    "url": "https://intake.your-agency.com/client-intake/65f2a9113df01c00127b682a?token=eyJhbGciOi…",
    "expiresAt": "2026-05-24T01:50:46.000Z"
}
CampoNotas
tokenO JWT também incorporado em url. Normalmente você não precisa usá-lo diretamente — os clientes abrem url e o servidor valida o token incorporado. O claim jti do token é o ID único do link; persista-o no seu sistema se quiser revogar este link específico mais tarde (ver Revogar um link de admissão de cliente).
urlO link compartilhável. Usa o seu domínio personalizado se houver um verificado e com SSL provisionado para o workspace; caso contrário, recorre a ds160.io. O prefixo de rota do idioma (/es, /cn, …) é definido com base em defaultLanguage.
expiresAtMarca de tempo ISO-8601. O registro correspondente é excluído automaticamente nesse ponto, de modo que os links não podem ser reutilizados após a expiração.

Envie a url ao seu cliente. O token na URL é de uso único e expira em expiresAt — não há autenticação adicional necessária para que o cliente o preencha.

Não existe um endpoint v1 para revogar links de cliente emitidos — revogue pelo painel (Workspace → Formulário → Compartilhamento → Revogar link). A revogação é imediata e permanente: a próxima solicitação que o link fizer retorna 401. Se houver suspeita de vazamento e ninguém estiver disponível para revogar pela UI, a alternativa mais segura é deixar o link expirar (limite expiresInDays adequadamente).

Valores válidos de defaultLanguage

defaultLanguage controla o idioma da interface de admissão do cliente quando o destinatário abre o link pela primeira vez. Use qualquer um dos códigos abaixo — qualquer outro valor retorna 400 Bad Request. Os códigos curtos (en, cn, …) são os identificadores de localização internos da DS160.io; a coluna BCP-47 mostra o que emitimos em <html lang>, hreflang e nas APIs Intl.*. Use o código curto nas solicitações à API; a forma BCP-47 é informativa.

CódigoIdiomaNome nativoBCP-47
enEnglishEnglishen-US
ruRussianРусскийru-RU
roRomanianRomânăro-RO
esSpanishEspañoles-ES
cnChinese中文zh-CN
viVietnameseTiếng Việtvi-VN
hiHindiहिन्दीhi-IN
nlDutchNederlandsnl-NL
ptPortuguesePortuguêspt-BR
frFrenchFrançaisfr-FR

A url é uma credencial portadora — qualquer pessoa que a possua pode preencher o formulário até que ele expire, sem segundo fator.

  • Envie por um canal de confiança; não a publique em lugares onde possa vazar (chat público, páginas indexadas por buscadores, pacotes compartilhados).
  • Oculte ?token= nos logs. O claim jti sozinho é seguro de registrar.
  • Revogue diante de suspeita de vazamento — a revogação é imediata e permanente.
  • expiresInDays é um compromisso: mais curto = janela de vazamento menor, mais longo = menos atrito para reemitir.

6. Listar ou obter formulários

# All forms in the workspace
curl https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms \
  -H "Authorization: Bearer $DS160_KEY"

# A single form by id
curl https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms/$FORM_ID \
  -H "Authorization: Bearer $DS160_KEY"

Estes endpoints de leitura aceitam o scope forms:read (somente leitura) ou forms:write (que também concede leitura).

Paginação

GET /v1/workspaces/:workspaceId/forms é paginado. Passe os parâmetros de query ?limit= e ?cursor= para percorrer os resultados:

Parâmetro de querySignificado
limitMáximo de formulários a retornar nesta página. Padrão 50, limite máximo 200.
cursorid do formulário a partir do nextCursor da página anterior. Omita na primeira solicitação para começar pelo formulário mais novo.

Cada resposta inclui um campo nextCursor. Quando não há mais páginas, nextCursor é null. Exemplo:

{
    "forms": [
        { "id": "65f2a911…", "name": "Smith / B1", "status": "in_progress", "workspaceId": "65f2a900…", "userId": "65f2a8f0…", "preferredConsulate": null, "createdAt": "2026-05-14T09:12:33.000Z", "archivedAt": null },
        { "id": "65f2a8a3…", "name": "Garcia / F1", "status": "completed", "workspaceId": "65f2a900…", "userId": "65f2a8f0…", "preferredConsulate": "MAD", "createdAt": "2026-05-13T18:01:09.000Z", "archivedAt": null }
    ],
    "nextCursor": "65f2a8a3…"
}

Avance enviando ?cursor=65f2a8a3…&limit=50 na próxima solicitação. Os formulários são ordenados do mais novo ao mais antigo por id, então um cursor fixa a sua posição de leitura mesmo que novos formulários sejam criados entre as chamadas — você não verá duplicatas nem perderá nada que existia no momento em que começou a paginar.

Valores de status do formulário

O campo status em cada resposta de metadados de formulário é um dos seguintes:

ValorQuando se aplica
not_startedO formulário foi criado (via API ou painel), mas nenhum campo foi respondido ainda.
in_progressPelo menos um campo foi respondido. A maioria dos formulários passa a maior parte da vida neste estado.
completedO solicitante terminou e enviou a admissão; a agência já pode baixar/enviar o PDF do DS-160.
archivedO formulário foi arquivado pelo painel. Excluído dos resultados de listagem padrão, a menos que você consulte explicitamente por itens arquivados.

O formato de transmissão usa underscores (in_progress, não in-progress). Comparar com as strings acima como estão sempre funcionará.

Acesso a PII via a API

Os endpoints de metadados de formulários v1 (GET /workspaces/:workspaceId/forms e GET /workspaces/:workspaceId/forms/:formId) retornam apenas metadados operacionais: id, name, status, workspaceId, userId, preferredConsulate, createdAt, archivedAt. Eles não expõem as respostas do solicitante — nenhum nome, data de nascimento, número de passaporte, histórico de viagens ou qualquer outro campo do DS-160 é acessível através de /v1.

Os PDFs do DS-160 concluídos e os dados completos dos campos são acessíveis apenas através do painel, sob o proprietário auditado do solicitante. Se a sua integração precisar ler dados inseridos pelo solicitante, faça isso através do seu próprio fluxo de admissão de cliente (o seu CRM coleta os dados primeiro, você os envia para a DS160.io) — nunca presuma que a API os devolverá.

Limites de taxa

Cada chave está limitada a 60 solicitações por minuto. Cada resposta inclui:

CabeçalhoSignificado
X-RateLimit-LimitO limite (60).
X-RateLimit-RemainingSolicitações restantes na janela atual.
X-RateLimit-ResetMarca de tempo Unix de quando a janela é reiniciada.

Se você exceder o limite, a API retorna 429 Too Many Requests com um cabeçalho Retry-After. Se a sua integração legitimamente precisa de um teto mais alto, entre em contato com o suporte — podemos elevar o limite para chaves específicas.

Idempotência e novas tentativas

A API v1 não suporta um cabeçalho Idempotency-Key. Concretamente:

  • POST /forms e POST /forms/:formId/clone não são idempotentes e consomem um crédito de formulário a cada chamada. Uma nova tentativa ingênua após um timeout de rede criará um formulário duplicado e queimará um segundo crédito.
  • POST /forms/:formId/client-links também não é idempotente, mas não consome créditos — uma chamada repetida simplesmente cunha um segundo link com o seu próprio jti e expiresAt.
  • Todos os endpoints GET são seguros para repetir livremente.

Padrão de nova tentativa recomendado para create/clone: se uma POST /forms (ou /clone) der timeout ou retornar um 5xx, não tente novamente às cegas. Em vez disso, chame GET /workspaces/:workspaceId/forms (os formulários são ordenados do mais novo ao mais antigo) e verifique se um formulário com o name que você forneceu foi criado nos últimos segundos. Se foi, trate a chamada original como bem-sucedida e pule a nova tentativa. Se não foi, a escrita original definitivamente não foi efetivada e uma nova tentativa é segura.

Podemos adicionar suporte a Idempotency-Key em um lançamento menor futuro; as integrações devem se planejar para isso, mas não depender disso hoje.

Revogar uma chave

Se uma chave for vazada, descontinuada ou simplesmente não usada, revogue-a em Configurações do Workspace → Equipe e acesso → Chaves de API → Revogar. A revogação entra em vigor imediatamente — a próxima solicitação que a chave fizer retorna 401 Unauthorized. Chaves revogadas permanecem na lista para fins de auditoria, mas não podem ser reativadas; gere uma nova se você precisar manter a integração funcionando.

Boas práticas

  • Uma chave por integração. Mais fácil de revogar cirurgicamente quando um sistema é desativado, e a coluna Last used no painel indica quais integrações ainda estão ativas.
  • Scopes mínimos. Uma chave que só gera links de cliente não deveria ter forms:clone. Scopes menores significam menor raio de impacto se o segredo vazar.
  • Guarde os segredos em um cofre. Nunca os comite no controle de versão nem os incorpore em um bundle de frontend — cada aba do navegador os exporia.
  • Rotacione periodicamente. Gere uma nova chave, migre a integração e então revogue a antiga. O painel mostra quando cada chave foi usada pela última vez para que você confirme a transição antes de revogar.

Erros

Todas as respostas não-2xx compartilham a mesma estrutura JSON:

{ "error": "human-readable message" }

O valor de error é uma string. Para 400 Bad Request por validação de esquema, é um array JSON serializado descrevendo cada restrição violada; para todo o resto, é uma mensagem curta que você pode exibir ou registrar diretamente.

StatusQuando ocorreCorpo de exemplo
400O corpo da solicitação ou os parâmetros de rota falharam na validação (campo obrigatório ausente, valor fora do intervalo, valor de enum desconhecido, etc.).{ "error": "[{\"keyword\":\"required\",\"params\":{\"missingProperty\":\"expiresInDays\"}}]" }
401Cabeçalho Authorization ausente, token Bearer malformado, o segredo não corresponde a nenhuma chave, ou a chave foi revogada.{ "error": "Missing API key" }  ·  { "error": "Invalid API key" }  ·  { "error": "API key revoked" }
402O workspace ficou sem créditos de formulário. Apenas create/clone. Recarregue o workspace e tente novamente.{ "error": "Workspace has no remaining credits" }
403A chave não tem o scope necessário, ou o :workspaceId na rota não corresponde ao workspace vinculado à chave.{ "error": "Missing required scope: forms:write" }  ·  { "error": "API key does not match workspace" }
404Formulário não encontrado, ou o formulário existe mas reside em um workspace diferente do da chave. (Retornado como 404 em vez de 403 para não vazar a existência entre workspaces.){ "error": "Form not found" }
429Limite de taxa por chave excedido (padrão 60 req/min). Espere até o valor Retry-After (segundos) e tente novamente.{ "error": "Rate limit exceeded" }
5xxErro transitório do servidor. Seguro repetir GETs livremente; para endpoints de escrita, ver Idempotência e novas tentativas antes de tentar novamente.

Exemplo de ponta a ponta

Um fluxo de admissão completo — criar um formulário, cunhar um link de cliente, enviá-lo e consultar a conclusão — usando curl. Integrações reais usarão o seu próprio cliente HTTP; a estrutura é a mesma em qualquer linguagem.

# 0. credentials (set once)
export DS160_KEY="..."
export DS160_WORKSPACE="65f2a900..."

# 1. Create a form. Capture the formId from the response.
FORM_ID=$(curl -s -X POST \
  https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms \
  -H "Authorization: Bearer $DS160_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Smith / B1 — 2026-05" }' \
  | jq -r .formId)

# 2. Mint a client intake link valid for 14 days.
LINK_JSON=$(curl -s -X POST \
  https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms/$FORM_ID/client-links \
  -H "Authorization: Bearer $DS160_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "expiresInDays": 14, "defaultLanguage": "en" }')

CLIENT_URL=$(echo "$LINK_JSON" | jq -r .url)

# 3. Send $CLIENT_URL to the applicant through your usual channel
#    (email, secure portal, etc.). The applicant fills in the form.

# 4. Poll for completion. Status transitions:
#    not_started → in_progress → completed
curl -s https://ds160.io/api/v1/workspaces/$DS160_WORKSPACE/forms/$FORM_ID \
  -H "Authorization: Bearer $DS160_KEY" \
  | jq .status
# → "completed" when the applicant has finished

# 5. Download the DS-160 PDF from the dashboard
#    (no v1 endpoint for this today).

Algumas notas sobre a receita:

  • Cadência de polling: a cada 5–15 minutos é suficiente. Com um teto de 60 req/min, uma única chave pode consultar confortavelmente milhares de formulários em andamento.
  • Os webhooks ainda não estão disponíveis. Se você precisa de notificações push na conclusão, acompanhe esta página ou entre em contato com o suporte — está no roadmap.
  • O passo 5 (baixar o PDF concluído) requer uma sessão do painel hoje. A API v1 intencionalmente não expõe as respostas do solicitante — ver Acesso a PII via a API.

Versionamento e suporte

Estabilidade. /v1 é a superfície estável da API. Adicionamos novos campos opcionais, novos endpoints e novos valores de enum dentro de /v1 sem incrementar a versão maior, mas não renomeamos, removemos nem alteramos o tipo de nenhum campo existente. Se algum dia precisarmos de uma mudança incompatível, ela será lançada sob /v2 e /v1 continuará funcionando por pelo menos 12 meses ao lado dela.

Adições retrocompatíveis que você deve planejar:

  • Novos campos opcionais nas solicitações aparecerão com o tempo. Solicitações existentes que não os enviarem continuarão funcionando.
  • Novos campos podem ser adicionados nas respostas. Trate os campos desconhecidos como ignoráveis — não falhe se um campo futuro aparecer.
  • Novos valores de enum para status, defaultLanguage e disabledSections serão adicionados. Não trave com valores desconhecidos; registre-os e prossiga.

Suporte:

  • Limites de taxa mais altos, domínios personalizados, acesso antecipado a webhooks e perguntas sobre integração: entre em contato com o seu gerente de conta ou support@ds160.io.
  • Relatos de bugs contra a API v1: o mesmo endereço. Cada resposta carrega um cabeçalho x-request-id — inclua-o no seu relato para que possamos encontrar a chamada exata em nossos logs.