Эта страница переведена с английского. Английская версия — источник истины: если что-то выглядит неточно, сверьтесь с ней. EN

API-ключи и программный доступ

Создавайте формы, клонируйте их и генерируйте ссылки для клиентов из вашего бэкенда с помощью API-ключей, привязанных к рабочему пространству.

Если у вас уже работает CRM, система управления делами или внутренний портал, не нужно заходить в панель и создавать каждую форму вручную. API-ключи позволяют вашему бэкенду вызывать v1-API DS160.io напрямую: создавать формы, клонировать их и генерировать клиентские ссылки в нужное вам время.

API-ключи доступны в рабочих пространствах типа Business. Каждый ключ привязан к одному рабочему пространству и действует только внутри него.

Базовый URL API

Все v1-эндпоинты имеют корень:

https://ds160.io/api/v1

Документация может хоститься на white-label-домене вашего агентства, но API-вызовы всегда уходят на ds160.io. Все примеры кода на этой странице используют полный URL, чтобы вы могли копипастить без переписывания.

Справочник эндпоинтов

/v1 стабилен — пути и имена полей не изменятся без мажорной версии /v2 (см. Версионирование и поддержка). Пути в таблице ниже показаны относительно префикса рабочего пространства /workspaces/:workspaceId/; полные формы запросов и ответов задокументированы в разделе каждого эндпоинта.

МетодПутьScopeТело запроса
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

Все тела запросов и ответы — JSON. Scope forms:read даёт только чтение метаданных форм; forms:write даёт и чтение, и запись, поэтому ключ, который только пишет, всё равно бесплатно получает чтение.

1. Создать API-ключ

Откройте Настройки рабочего пространства → API-ключи и нажмите Создать API-ключ. Выберите описательное имя (рекомендуется один ключ на интеграцию, например Production CRM или Staging webhook handler), затем выберите нужные scope:

  • forms:read — только читать метаданные форм (список / получение)
  • forms:write — создавать формы; также даёт чтение
  • forms:clone — дублировать существующую форму
  • client-links:write — генерировать клиентские ссылки

Создавать ключи могут только Владелец и Администратор рабочего пространства.

После нажатия Создать ключ платформа показывает полный секрет один раз. Сразу скопируйте его в менеджер секретов — после закрытия модального окна будут видны только последние четыре символа. Если ключ потерян, отзовите его и создайте новый.

2. Аутентификация

Все эндпоинты v1 ожидают заголовок 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 рабочего пространства появляется в URL каждой страницы панели (/workspaces/<workspaceId>/...). Рабочее пространство ключа связано на стороне сервера: вызов эндпоинта другого пространства с неподходящим ключом возвращает 403 Forbidden.

Совет: вкладки языка синхронизированы по всем фрагментам этой страницы. Выберите свой язык один раз — остальная часть страницы последует за вами.

3. Создать форму

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

Каждый вызов расходует один кредит формы из вашего тарифного плана (как и создание формы из панели). Если в рабочем пространстве закончились кредиты, вызов вернёт 402 Payment Required — пополните баланс перед повтором.

Необязательное поле name задаёт читаемое название формы (до 200 символов). Если опустить его, имя будет сгенерировано автоматически — в любом случае вы сможете переименовать форму позже из панели.

4. Клонировать существующую форму

Если у вас есть форма-шаблон, которую вы часто переиспользуете (например, та же программа J-1 одного работодателя), клонируйте её вместо создания с нуля. Опционально передайте disabledSections, чтобы пропустить определённые страницы — всё перечисленное заменяется пустыми полями в новой форме, чтобы клиент заполнил их заново:

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

Клонирование расходует один кредит формы, как и создание новой.

Допустимые значения disabledSections

Передайте пустой массив (или вообще опустите поле), чтобы скопировать все страницы. Иначе используйте любую комбинацию идентификаторов ниже — любое другое значение вернёт 400 Bad Request.

ИдентификаторСтраница
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

В форме реально присутствуют только разделы, относящиеся к её категории визы; перечисление страницы, которой нет в исходной форме, ни на что не влияет.

5. Сгенерировать клиентскую ссылку

Когда форма существует, сгенерируйте URL с токеном, которым клиент может воспользоваться для заполнения:

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

Тело запроса

ПолеОбязательноЗаметки
expiresInDaysдаЦелое число 1–365. Значения по умолчанию нет — пропуск возвращает 400 Bad Request. expiresAt ссылки вычисляется как now + expiresInDays, а соответствующая запись токена автоматически удаляется в этот момент.
defaultLanguageдаОдин из кодов в Допустимые значения defaultLanguage. Задаёт префикс локали в возвращаемом url и язык, на котором открывается форма.
hideBrandingнетБулево. Если true, форма показывается без брендинга — логотип и тема whitelabel агентства скрываются для этой ссылки. По умолчанию false (если whitelabel настроен в рабочем пространстве, он отображается).

Пример ответа (200 OK)

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2NmMxYmQ0ZjZmNGFkNTQwMzMxNDhmYmEiLCJmb3JtSWQiOiI2NWYyYTkxMTNkZjAxYzAwMTI3YjY4MmEiLCJ3b3Jrc3BhY2VJZCI6IjY1ZjJhOTAwM2RmMDFjMDAxMjdiNjgwYSIsImV4cCI6MTc0ODA0ODI0NywiaWF0IjoxNzQ3NDQzNDQ3LCJkZWZhdWx0TGFuZ3VhZ2UiOiJlbiIsImhpZGVCcmFuZGluZyI6ZmFsc2V9.SiGNATuRe",
    "url": "https://intake.your-agency.com/client-intake/65f2a9113df01c00127b682a?token=eyJhbGciOi…",
    "expiresAt": "2026-05-24T01:50:46.000Z"
}
ПолеЗаметки
tokenJWT, также встроенный в url. Обычно использовать его напрямую не нужно — клиенты открывают url, и сервер валидирует встроенный токен. Claim jti токена — уникальный ID ссылки; сохраните его в своей системе, если может понадобиться отозвать именно эту ссылку позже (см. Отзыв клиентской ссылки).
urlШарируемая ссылка. Использует ваш кастомный домен, если он подтверждён и SSL подготовлен для рабочего пространства; иначе откатывается к ds160.io. Префикс пути для локали (/es, /cn, …) выставляется по defaultLanguage.
expiresAtISO-8601 timestamp. Соответствующая запись автоматически удаляется в этот момент, поэтому ссылки нельзя использовать повторно после истечения срока.

Отправьте url своему клиенту. Токен в URL одноразовый и истекает в expiresAt — дополнительной аутентификации для заполнения формы клиенту не требуется.

Отзыв клиентской ссылки

v1-эндпоинта для отзыва выпущенных клиентских ссылок не существует — отзывайте из панели (Рабочее пространство → Форма → Поделиться → Отозвать ссылку). Отзыв немедленный и необратимый: следующий запрос по ссылке вернёт 401. Если есть подозрение на утечку и нет человека, который мог бы отозвать из UI, самая безопасная резервная стратегия — дать ссылке истечь (ограничьте expiresInDays соответствующе).

Допустимые значения defaultLanguage

defaultLanguage управляет языком интерфейса заполнения формы, когда получатель впервые открывает ссылку. Используйте любой из кодов ниже — любое другое значение вернёт 400 Bad Request. Короткие коды (en, cn, …) — внутренние идентификаторы локалей DS160.io; колонка BCP-47 показывает, что мы выдаём в <html lang>, hreflang и API Intl.*. В API-запросах используйте короткий код; форма BCP-47 — для информации.

КодЯзыкСамоназваниеBCP-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

Относитесь к клиентским ссылкам как к паролям

url — это bearer-учётка: любой, у кого она есть, может заполнить форму до истечения срока, без второго фактора.

  • Отправляйте по доверенному каналу; не публикуйте там, откуда возможны утечки (публичные чаты, индексируемые поисковиками страницы, общие коллекции).
  • Маскируйте ?token= в логах. Claim jti сам по себе безопасно логировать.
  • Отзывайте при подозрении на утечку — отзыв немедленный и необратимый.
  • expiresInDays — компромисс: меньше = меньше окно утечки, больше = меньше трений с перевыпуском.

6. Получить список форм или одну форму

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

Эти эндпоинты чтения принимают scope forms:read (только чтение) или forms:write (даёт и чтение).

Постраничный вывод

GET /v1/workspaces/:workspaceId/forms поддерживает постраничный вывод. Используйте query-параметры ?limit= и ?cursor=, чтобы пройти по результатам:

ПараметрЗначение
limitМаксимум форм на странице. По умолчанию 50, жёсткий лимит 200.
cursorid формы из nextCursor предыдущей страницы. Не указывайте при первом запросе, чтобы начать с самой свежей формы.

Каждый ответ содержит поле nextCursor. Когда страниц больше нет, nextCursor равен null. Пример:

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

Чтобы перейти к следующей странице, отправьте ?cursor=65f2a8a3…&limit=50 в следующем запросе. Формы отсортированы от новых к старым по id, поэтому курсор фиксирует позицию чтения, даже если между вызовами создаются новые формы — вы не увидите дубликатов и не пропустите ничего из того, что существовало на момент начала пагинации.

Значения status формы

Поле status в каждом ответе с метаданными формы — одно из:

ЗначениеКогда применяется
not_startedФорма создана (через API или из панели), но ни одно поле ещё не заполнено.
in_progressХотя бы одно поле заполнено. Большую часть жизни форма проводит в этом состоянии.
completedЗаявитель завершил и отправил анкету; агентство теперь может скачать/подать PDF DS-160.
archivedФорма была архивирована из панели. Исключена из стандартного списка, если только вы явно не запрашиваете архивные элементы.

Формат провода использует подчёркивания (in_progress, а не in-progress). Сравнение с приведёнными выше строками как есть всегда сработает.

Доступ к PII через API

v1-эндпоинты метаданных форм (GET /workspaces/:workspaceId/forms и GET /workspaces/:workspaceId/forms/:formId) возвращают только операционные метаданные: id, name, status, workspaceId, userId, preferredConsulate, createdAt, archivedAt. Они не раскрывают ответы заявителя — никакие имена, даты рождения, номера паспортов, истории поездок или другие поля DS-160 недоступны через /v1.

Заполненные PDF-файлы DS-160 и полные данные полей доступны только через панель, под аудит-логированным владельцем заявителя. Если вашей интеграции нужно читать данные, введённые заявителем, делайте это через свой собственный поток приёма клиента (ваш CRM сначала собирает данные, а вы передаёте их в DS160.io) — никогда не предполагайте, что API вернёт их обратно.

Лимиты запросов

Каждый ключ ограничен 60 запросами в минуту. Каждый ответ содержит:

ЗаголовокЗначение
X-RateLimit-LimitВерхняя граница (60).
X-RateLimit-RemainingОставшиеся запросы в текущем окне.
X-RateLimit-ResetUnix-метка времени, когда окно сбрасывается.

При превышении лимита API возвращает 429 Too Many Requests с заголовком Retry-After. Если вашей интеграции действительно нужен потолок выше, свяжитесь с поддержкой — мы можем поднять лимит для конкретных ключей.

Идемпотентность и повторы

v1 API не поддерживает заголовок Idempotency-Key. Конкретно:

  • POST /forms и POST /forms/:formId/clone не идемпотентны и расходуют один кредит формы при каждом вызове. Наивный повтор после сетевого таймаута создаст дубликат формы и сожжёт второй кредит.
  • POST /forms/:formId/client-links тоже не идемпотентен, но не расходует кредиты — повторный вызов просто выпустит вторую ссылку со своим jti и expiresAt.
  • Все эндпоинты GET можно свободно повторять.

Рекомендуемый шаблон повтора для create/clone: если POST /forms (или /clone) истёк по таймауту или вернул 5xx, не повторяйте слепо. Вместо этого вызовите GET /workspaces/:workspaceId/forms (формы отсортированы от новых к старым) и проверьте, не была ли создана форма с указанным вами name в последние несколько секунд. Если да — считайте оригинальный вызов успешным и пропустите повтор. Если нет — исходная запись точно не прошла, и повтор безопасен.

В будущем минорном релизе мы можем добавить поддержку Idempotency-Key; интеграциям стоит это планировать, но не зависеть от этого сегодня.

Отзыв ключа

Если ключ скомпрометирован, выводится из эксплуатации или просто не используется, отзовите его через Настройки рабочего пространства → API-ключи → Отозвать. Отзыв действует немедленно — следующий запрос с этим ключом вернёт 401 Unauthorized. Отозванные ключи остаются в списке для аудита, но не могут быть восстановлены; выпустите новый, если интеграции нужно продолжать работу.

Лучшие практики

  • Один ключ на интеграцию. Точнее отзывать при выводе системы из эксплуатации, а колонка Last used в панели показывает, какие интеграции ещё активны.
  • Минимально необходимые scope. Ключу, который только генерирует клиентские ссылки, не нужен forms:clone. Меньше прав — меньше радиус поражения при утечке секрета.
  • Храните секреты в vault. Никогда не коммитьте их в систему контроля версий и не встраивайте в бандл фронтенда — каждая вкладка браузера их раскроет.
  • Ротируйте по расписанию. Выпустите новый ключ, переключите интеграцию, потом отзовите старый. Панель показывает, когда ключ использовался последний раз, чтобы подтвердить переключение перед отзывом.

Ошибки

Все ответы со статусом не из 2xx имеют единую JSON-форму:

{ "error": "сообщение для человека" }

Значение error — строка. Для 400 Bad Request из валидации схемы это сериализованный JSON-массив с описанием каждого нарушения; для всего остального — короткое сообщение, которое можно показать пользователю или записать в лог напрямую.

СтатусКогда возникаетПример тела
400Тело запроса или параметры пути не прошли валидацию (отсутствует обязательное поле, значение вне диапазона, неизвестное значение enum и т. п.).{ "error": "[{\"keyword\":\"required\",\"params\":{\"missingProperty\":\"expiresInDays\"}}]" }
401Отсутствует заголовок Authorization, неправильный Bearer-токен, секрет не совпадает ни с одним ключом, или ключ отозван.{ "error": "Missing API key" }  ·  { "error": "Invalid API key" }  ·  { "error": "API key revoked" }
402В рабочем пространстве закончились кредиты форм. Только create/clone. Пополните пространство и повторите.{ "error": "Workspace has no remaining credits" }
403У ключа нет нужного scope, или :workspaceId в пути не совпадает с привязанным к ключу рабочим пространством.{ "error": "Missing required scope: forms:write" }  ·  { "error": "API key does not match workspace" }
404Форма не найдена, или форма существует, но в другом рабочем пространстве. (Возвращается как 404, а не 403, чтобы не утекало существование между пространствами.){ "error": "Form not found" }
429Превышен лимит запросов на ключ (по умолчанию 60 req/min). Подождите до значения Retry-After (секунды) и повторите.{ "error": "Rate limit exceeded" }
5xxВременная ошибка сервера. GET можно повторять свободно; для записывающих эндпоинтов сначала см. Идемпотентность и повторы.

Сквозной пример

Полный сценарий — создаём форму, выпускаем клиентскую ссылку, отправляем её и опрашиваем завершение — с использованием curl. Реальные интеграции будут использовать собственный HTTP-клиент; форма одинакова на любом языке.

# 0. учётные данные (задаются один раз)
export DS160_KEY="..."
export DS160_WORKSPACE="65f2a900..."

# 1. Создать форму. Сохранить formId из ответа.
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. Выпустить клиентскую ссылку на 14 дней.
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. Отправить $CLIENT_URL заявителю своим обычным каналом
#    (e-mail, защищённый портал и т. п.). Заявитель заполняет форму.

# 4. Опрос завершения. Переходы статуса:
#    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", когда заявитель закончил

# 5. Скачать PDF DS-160 из панели
#    (v1-эндпоинта для этого сегодня нет).

Несколько замечаний к сценарию:

  • Частота опроса: каждые 5–15 минут вполне достаточно. При потолке 60 req/min один ключ комфортно опрашивает тысячи активных форм.
  • Webhook-и пока недоступны. Если нужны push-уведомления о завершении — следите за этой страницей или обратитесь в поддержку, это в дорожной карте.
  • Шаг 5 (скачивание готового PDF) сегодня требует сессии в панели. v1 API намеренно не раскрывает ответы заявителя — см. Доступ к PII через API.

Версионирование и поддержка

Стабильность. /v1 — стабильная поверхность API. Мы добавляем новые опциональные поля, новые эндпоинты и новые значения enum в /v1 без повышения мажорной версии, но не переименовываем, не удаляем и не меняем типы существующих полей. Если когда-либо потребуется ломающее изменение — оно выйдет под /v2, а /v1 продолжит работать ещё как минимум 12 месяцев параллельно.

Обратно-совместимые дополнения, к которым стоит подготовиться:

  • Со временем будут появляться новые опциональные поля запросов. Существующие запросы, которые их не отправляют, продолжат работать.
  • В ответы могут добавляться новые поля. Считайте неизвестные поля игнорируемыми — не падайте, если появится поле из будущего.
  • Будут добавляться новые значения enum для status, defaultLanguage и disabledSections. Не падайте на неизвестных значениях; логируйте и продолжайте.

Поддержка:

  • Повышение лимитов, кастомные домены, ранний доступ к webhook-ам и вопросы по интеграции: свяжитесь со своим аккаунт-менеджером или support@ds160.io.
  • Баг-репорты на v1 API: тот же адрес. Каждый ответ содержит заголовок x-request-id — включайте его в отчёт, чтобы мы могли найти конкретный вызов в логах.