Această pagină a fost tradusă din engleză. Versiunea în engleză este sursa de adevăr — dacă ceva pare incorect, verifică-o pe aceea. EN

Chei API și acces programatic

Creează formulare, clonează-le și generează linkuri de admisie pentru clienți din propriul tău backend cu chei API delimitate la workspace.

Dacă deja folosești un CRM, un sistem de management al cazurilor sau un portal intern, nu trebuie să intri în panou pentru a crea fiecare formular. Cheile API permit backendului tău să apeleze direct API-ul v1 DS160.io: creezi formulare, le clonezi și generezi linkuri de admisie pentru clienți în propriul tău ritm.

Cheile API sunt disponibile pentru workspace-urile de tip Business. Fiecare cheie este legată de un singur workspace și poate acționa doar în interiorul acestuia.

URL de bază al API-ului

Toate endpoint-urile v1 au rădăcina:

https://ds160.io/api/v1

Aceste documente pot fi găzduite pe domeniul white-label al agenției tale, dar apelurile API merg întotdeauna la ds160.io. Toate exemplele de cod din această pagină folosesc URL-ul complet, ca să poți copia-lipi fără rescriere.

Referință de endpoint-uri

/v1 este stabil — căile și numele câmpurilor nu se vor schimba fără o versiune majoră /v2 (vezi Versionare și suport). Căile din tabelul de mai jos sunt afișate relativ la prefixul workspace-ului /workspaces/:workspaceId/; formele complete ale cererilor și răspunsurilor sunt documentate în secțiunea fiecărui endpoint.

MetodăCaleScopeCorpul cererii
POST/formsforms:writename?
POST/forms/:formId/cloneforms:clonedisabledSections?
POST/forms/:formId/client-linksclient-links:writeexpiresInDays, defaultLanguage, hideBranding?
GET/formsforms:read / forms:writequery: limit?, cursor?
GET/forms/:formIdforms:read / forms:write

Toate corpurile și răspunsurile sunt JSON. Scope-ul forms:read acordă acces doar de citire la metadatele de formular; forms:write acordă atât citire cât și scriere, așa că o cheie care doar scrie are oricum acces de citire gratuit.

1. Creează o cheie API

Deschide Setări spațiu de lucru → Chei API și apasă Creează cheie API. Alege un nume descriptiv (recomandăm o cheie per integrare, de ex. Production CRM sau Staging webhook handler), apoi selectează scope-urile de care integrarea are nevoie:

  • forms:read — doar citește metadatele formularelor (listă / preluare)
  • forms:write — creează formulare; acordă și citire
  • forms:clone — duplichează un formular existent
  • client-links:write — generează linkuri de admisie pentru clienți

Doar Proprietar și Administrator din workspace pot genera chei.

Când apeși Creează cheie, platforma îți arată secretul complet o singură dată. Copiază-l imediat în managerul tău de secrete — după închiderea modalului se vor afișa doar ultimele patru caractere. Dacă pierzi o cheie, revoc-o și generează una nouă.

2. Autentifică-te

Toate endpoint-urile v1 așteaptă un header 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"

ID-ul workspace-ului apare în URL-ul fiecărei pagini din panou (/workspaces/<workspaceId>/...). Workspace-ul cheii este legat pe server — apelarea unui endpoint din alt workspace cu cheia greșită returnează 403 Forbidden.

Sfat: tab-urile de limbaj se sincronizează între toate fragmentele de pe această pagină. Alege-ți limbajul o singură dată și restul paginii te va urma.

3. Creează un formular

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…" }

Fiecare apel consumă un credit de formular din planul tău de facturare (la fel ca atunci când creezi un formular din panou). Dacă workspace-ul rămâne fără credite, apelul returnează 402 Payment Required — alimentează contul înainte de a încerca din nou.

Câmpul opțional name setează o etichetă lizibilă pentru formular (până la 200 de caractere). Omite-l pentru a obține un nume generat automat — în oricare caz, poți redenumi formularul ulterior din panou.

4. Clonează un formular existent

Dacă ai un formular-șablon pe care îl reemiți frecvent (de ex. același program J-1 al aceluiași angajator), clonează-l în loc să o iei de la zero. Opțional, transmite disabledSections pentru a omite anumite pagini — orice pagină listată este înlocuită cu câmpuri goale în noul formular, astfel încât clientul să o completeze de la 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…" }

Clonarea consumă un credit de formular la fel ca o creare nouă.

Valori valide pentru disabledSections

Trimite un array gol (sau omite câmpul) pentru a copia toate paginile. Altfel, folosește orice combinație din identificatorii de mai jos — orice altă valoare returnează 400 Bad Request.

IdentificatorPagină
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

Doar secțiunile relevante pentru categoria de viză a formularului sunt efectiv prezente; listarea unei pagini care nu există în formularul sursă nu are niciun efect.

Odată ce există un formular, generează un URL cu token pe care clientul să-l folosească pentru completare:

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" }'

Corpul cererii

CâmpObligatoriuNote
expiresInDaysdaÎntreg 1–365. Fără valoare implicită — omiterea returnează 400 Bad Request. expiresAt al linkului se calculează ca now + expiresInDays și înregistrarea de token subiacentă se șterge automat în acel moment.
defaultLanguagedaUnul dintre codurile din Valori valide pentru defaultLanguage. Setează prefixul de localizare în url-ul returnat și limba în care se deschide formularul.
hideBrandingnuBoolean. Când e true, formularul este afișat fără branding — logo-ul și tema whitelabel ale agenției sunt suprimate pentru acest link. Implicit false (se afișează chrome-ul whitelabel al workspace-ului dacă este configurat).

Răspuns de exemplu (200 OK)

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2NmMxYmQ0ZjZmNGFkNTQwMzMxNDhmYmEiLCJmb3JtSWQiOiI2NWYyYTkxMTNkZjAxYzAwMTI3YjY4MmEiLCJ3b3Jrc3BhY2VJZCI6IjY1ZjJhOTAwM2RmMDFjMDAxMjdiNjgwYSIsImV4cCI6MTc0ODA0ODI0NywiaWF0IjoxNzQ3NDQzNDQ3LCJkZWZhdWx0TGFuZ3VhZ2UiOiJlbiIsImhpZGVCcmFuZGluZyI6ZmFsc2V9.SiGNATuRe",
    "url": "https://intake.your-agency.com/client-intake/65f2a9113df01c00127b682a?token=eyJhbGciOi…",
    "expiresAt": "2026-05-24T01:50:46.000Z"
}
CâmpNote
tokenJWT-ul încorporat și în url. În mod normal nu trebuie să-l folosești direct — clienții deschid url, iar serverul validează tokenul încorporat. Claim-ul jti al tokenului este ID-ul unic al linkului; păstrează-l în sistemul tău dacă s-ar putea să trebuiască să revoci ulterior acest link specific (vezi Revocarea unui link de admisie pentru client).
urlLinkul partajabil. Folosește domeniul tău personalizat dacă este verificat și are SSL pentru workspace; altfel revine la ds160.io. Prefixul de locale al rutei (/es, /cn, …) se setează pe baza defaultLanguage.
expiresAtTimestamp ISO-8601. Înregistrarea corespunzătoare se șterge automat la momentul respectiv, așa că linkurile nu pot fi reutilizate după expirare.

Trimite url clientului tău. Tokenul din URL este de unică folosință și expiră la expiresAt — nu este necesară nicio altă autentificare pentru ca clientul să-l completeze.

Nu există un endpoint v1 pentru revocarea linkurilor de client emise — revocă din panou (Workspace → Formular → Partajare → Revocă link). Revocarea este imediată și permanentă: următoarea cerere pe link returnează 401. Dacă se suspectează o scurgere și nu există cineva disponibil pentru a revoca din UI, alternativa cea mai sigură este să lași linkul să expire (limitează expiresInDays corespunzător).

Valori valide pentru defaultLanguage

defaultLanguage controlează limba interfeței de admisie atunci când destinatarul deschide pentru prima dată linkul. Folosește oricare din codurile de mai jos — orice altă valoare returnează 400 Bad Request. Codurile scurte (en, cn, …) sunt identificatorii interni de localizare ai DS160.io; coloana BCP-47 arată ce emitem în <html lang>, hreflang și API-urile Intl.*. Folosește codul scurt în cererile API; forma BCP-47 este informativă.

CodLimbăNume nativBCP-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

Tratează linkurile clienților ca pe parole

url este o credențială de tip bearer — oricine îl deține poate completa formularul până la expirare, fără un al doilea factor.

  • Trimite-l printr-un canal de încredere; nu-l publica acolo unde poate fi expus (chat public, pagini indexate, colecții partajate).
  • Maschează ?token= în loguri. Claim-ul jti poate fi logat singur fără probleme.
  • Revocă la suspiciunea de scurgere — revocarea este imediată și permanentă.
  • expiresInDays este un compromis: mai scurt = fereastră mai mică de expunere, mai lung = mai puțină nevoie de reemitere.

6. Listare sau preluare de formulare

# 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"

Aceste endpoint-uri de citire acceptă scope-ul forms:read (doar citire) sau forms:write (care acordă și citire).

Paginare

GET /v1/workspaces/:workspaceId/forms este paginat. Transmite parametrii de query ?limit= și ?cursor= pentru a parcurge rezultatele:

ParametruSemnificație
limitNumărul maxim de formulare returnate pe această pagină. Implicit 50, plafon 200.
cursorid al formularului din nextCursor al paginii anterioare. Omite-l la prima cerere pentru a porni de la cel mai recent formular.

Fiecare răspuns include un câmp nextCursor. Când nu mai sunt pagini, nextCursor este null. Exemplu:

{
    "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…"
}

Avansează trimițând ?cursor=65f2a8a3…&limit=50 la următoarea cerere. Formularele sunt sortate de la cel mai nou la cel mai vechi după id, așa că un cursor îți fixează poziția de citire chiar dacă se creează formulare noi între apeluri — nu vei vedea duplicate și nu vei rata nimic care exista în momentul în care ai început paginarea.

Valorile status ale unui formular

Câmpul status din fiecare răspuns de metadate de formular este unul dintre:

ValoareCând se aplică
not_startedFormularul a fost creat (via API sau panou), dar niciun câmp nu a fost completat încă.
in_progressCel puțin un câmp a fost completat. Majoritatea formularelor își petrec cea mai mare parte a vieții în această stare.
completedSolicitantul a terminat și a trimis admiterea; agenția poate acum descărca/depune PDF-ul DS-160.
archivedFormularul a fost arhivat din panou. Exclus din rezultatele implicite de listare, cu excepția cazului în care interoghezi explicit pentru elemente arhivate.

Formatul de pe fir folosește underscore-uri (in_progress, nu in-progress). Comparațiile cu șirurile de mai sus, ca atare, funcționează întotdeauna.

Acces PII prin API

Endpoint-urile v1 de metadate de formular (GET /workspaces/:workspaceId/forms și GET /workspaces/:workspaceId/forms/:formId) returnează doar metadate operaționale: id, name, status, workspaceId, userId, preferredConsulate, createdAt, archivedAt. Nu expun răspunsurile solicitantului — niciun nume, dată de naștere, număr de pașaport, istoric de călătorii sau alt câmp DS-160 nu este accesibil prin /v1.

PDF-urile DS-160 completate și datele complete de câmp sunt accesibile doar prin panou, sub proprietarul solicitantului cu jurnal de audit. Dacă integrarea ta trebuie să citească date introduse de solicitant, fă-o prin propriul tău flux de admisie a clientului (CRM-ul tău colectează datele întâi, apoi le împingi în DS160.io) — nu presupune niciodată că API-ul ți le va returna.

Limite de rată

Fiecare cheie este limitată la 60 de cereri pe minut. Fiecare răspuns conține:

HeaderSemnificație
X-RateLimit-LimitPlafonul (60).
X-RateLimit-RemainingCereri rămase în fereastra curentă.
X-RateLimit-ResetTimestamp Unix la care fereastra se resetează.

Dacă depășești limita, API-ul returnează 429 Too Many Requests cu un header Retry-After. Dacă integrarea ta are nevoie legitimă de un plafon mai mare, contactează suport — putem ridica limita pentru chei specifice.

Idempotență și reîncercări

API-ul v1 nu suportă un header Idempotency-Key. Concret:

  • POST /forms și POST /forms/:formId/clone nu sunt idempotente și consumă un credit de formular la fiecare apel. O reîncercare naivă după un timeout de rețea va crea un formular duplicat și va arde un al doilea credit.
  • POST /forms/:formId/client-links nu este nici el idempotent, dar nu consumă credite — un apel reîncercat pur și simplu emite un al doilea link cu propriul jti și expiresAt.
  • Toate endpoint-urile GET sunt sigure de reîncercat liber.

Pattern recomandat de reîncercare pentru create/clone: dacă o POST /forms (sau /clone) face timeout sau returnează 5xx, nu reîncerca orbește. În schimb, apelează GET /workspaces/:workspaceId/forms (formularele sunt sortate cel mai nou întâi) și verifică dacă s-a creat un formular cu name-ul pe care l-ai furnizat în ultimele câteva secunde. Dacă da, tratează apelul original ca reușit și sari peste reîncercare. Dacă nu, scrierea originală nu a ajuns sigur și reîncercarea e sigură.

S-ar putea să adăugăm suport pentru Idempotency-Key într-o versiune minoră viitoare; integrările ar trebui să planifice pentru asta, dar să nu depindă de ea astăzi.

Revocarea unei chei

Dacă o cheie este compromisă, ieșită din uz sau pur și simplu neutilizată, revoc-o din Setări spațiu de lucru → Chei API → Revocă. Revocarea are efect imediat — următoarea cerere cu acea cheie va returna 401 Unauthorized. Cheile revocate rămân în listă în scop de audit, dar nu pot fi reactivate; emite una nouă dacă integrarea trebuie să rămână funcțională.

Bune practici

  • O cheie per integrare. Mai ușor de revocat chirurgical când un sistem este retras, iar coloana Last used din panou îți spune care integrări sunt încă active.
  • Scope-uri minime. O cheie care doar generează linkuri pentru clienți nu ar trebui să aibă forms:clone. Scope-uri mai mici înseamnă rază mai mică de impact dacă secretul se scurge.
  • Păstrează secretele într-un vault. Niciodată să nu le pui în controlul versiunilor sau să le incluzi într-un bundle frontend — fiecare tab de browser le-ar expune.
  • Rotește după un program. Emite o cheie nouă, comută integrarea, apoi revoc-o pe cea veche. Panoul arată când a fost folosită ultima dată fiecare cheie, ca să confirmi comutarea înainte de a revoca.

Erori

Toate răspunsurile non-2xx împart aceeași structură JSON:

{ "error": "mesaj inteligibil pentru oameni" }

Valoarea error este un șir. Pentru 400 Bad Request din validarea de schemă, este un array JSON serializat care descrie fiecare constrângere încălcată; pentru orice altceva, este un mesaj scurt pe care îl poți afișa sau înregistra direct.

StatusCând apareCorp de exemplu
400Corpul cererii sau parametrii din cale au eșuat validarea (câmp obligatoriu lipsă, valoare în afara intervalului, valoare enum necunoscută, etc.).{ "error": "[{\"keyword\":\"required\",\"params\":{\"missingProperty\":\"expiresInDays\"}}]" }
401Lipsește header-ul Authorization, token Bearer malformat, secretul nu se potrivește cu nicio cheie, sau cheia a fost revocată.{ "error": "Missing API key" }  ·  { "error": "Invalid API key" }  ·  { "error": "API key revoked" }
402Workspace-ul a rămas fără credite de formular. Doar create/clone. Alimentează workspace-ul și reia.{ "error": "Workspace has no remaining credits" }
403Cheia nu are scope-ul necesar, sau :workspaceId din cale nu corespunde workspace-ului legat de cheie.{ "error": "Missing required scope: forms:write" }  ·  { "error": "API key does not match workspace" }
404Formular negăsit, sau formularul există dar trăiește într-un workspace diferit de cel al cheii. (Returnat ca 404 în loc de 403 pentru a nu scurge existența între workspace-uri.){ "error": "Form not found" }
429Limită de rată pe cheie depășită (implicit 60 req/min). Așteaptă până la valoarea Retry-After (secunde) și reîncearcă.{ "error": "Rate limit exceeded" }
5xxEroare tranzitorie de server. Sigur de reîncercat GET-urile liber; pentru endpoint-uri de scriere, vezi Idempotență și reîncercări înainte de a reîncerca.

Exemplu end-to-end

Un flux complet de admisie — creează un formular, emite un link de client, trimite-l și verifică finalizarea — folosind curl. Integrările reale vor folosi propriul lor client HTTP; forma este aceeași în orice limbă.

# 0. credențiale (setează o dată)
export DS160_KEY="..."
export DS160_WORKSPACE="65f2a900..."

# 1. Creează un formular. Capturează formId din răspuns.
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. Emite un link de client valabil 14 zile.
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. Trimite $CLIENT_URL solicitantului prin canalul tău obișnuit
#    (email, portal securizat, etc.). Solicitantul completează formularul.

# 4. Verifică finalizarea. Tranziții de stare:
#    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" când solicitantul a terminat

# 5. Descarcă PDF-ul DS-160 din panou
#    (fără endpoint v1 pentru asta astăzi).

Câteva note despre rețetă:

  • Cadența de polling: la fiecare 5–15 minute este suficient. Cu un plafon de 60 req/min, o singură cheie poate poll-a confortabil mii de formulare în curs.
  • Webhook-urile nu sunt încă disponibile. Dacă ai nevoie de notificări push la finalizare, urmărește această pagină sau contactează suport — este pe foaia de parcurs.
  • Pasul 5 (descărcarea PDF-ului finalizat) necesită o sesiune de panou astăzi. API-ul v1 nu expune intenționat răspunsurile solicitantului — vezi Acces PII prin API.

Versionare și suport

Stabilitate. /v1 este suprafața stabilă a API-ului. Adăugăm noi câmpuri opționale, noi endpoint-uri și noi valori enum în /v1 fără a incrementa versiunea majoră, dar nu redenumim, eliminăm sau schimbăm tipul niciunui câmp existent. Dacă vreodată va trebui să facem o schimbare incompatibilă, va apărea sub /v2 și /v1 va continua să funcționeze cel puțin 12 luni alături.

Adăugări retrocompatibile pentru care ar trebui să planifici:

  • În timp vor apărea noi câmpuri opționale în cereri. Cererile existente care nu le trimit vor continua să funcționeze.
  • Pot fi adăugate noi câmpuri în răspunsuri. Tratează câmpurile necunoscute ca ignorabile — nu eșua dacă apare un câmp viitor.
  • Vor fi adăugate noi valori enum pentru status, defaultLanguage și disabledSections. Nu crăpa pe valori necunoscute; loghează și continuă.

Suport:

  • Limite de rată mai mari, domenii personalizate, acces timpuriu la webhook-uri și întrebări de integrare: contactează managerul tău de cont sau support@ds160.io.
  • Rapoarte de bug-uri împotriva API-ului v1: aceeași adresă. Fiecare răspuns poartă un header x-request-id — include-l în raportul tău, ca să putem găsi apelul exact în logurile noastre.