Don't type personal details, such as a client's name.
Connecting an AI Agent (MCP)
Connect Claude, ChatGPT, Cursor or your own agent to your business workspace through MCP, so it can prepare DS-160 forms from a client's documents and send client links.
An immigration firm’s own AI agent can work in its DS160.io workspace through MCP (the Model Context Protocol). From a client’s documents or emails, the agent can create a form, fill in the answers it finds, check what is still missing, and send the client a link to complete the rest. Your staff then check the application and sign it on CEAC themselves.
The agent connects to one business workspace and can do only what you allow it to. Each thing it can do is also an endpoint of the DS160.io API.
Before you start
- A business workspace. MCP is available on business workspaces.
- Owner or Admin access. Only a workspace’s Owners and Admins can create API keys or allow a connection.
- An MCP client. Claude Code, Cursor, VS Code, Codex and Windsurf connect with an API key. claude.ai and ChatGPT connect by signing in to DS160.io.
The server’s address is:
https://www.ds160.io/api/mcp
Use this address even if your workspace has its own domain.
Connect with an API key
- Open Workspace Settings → Team & access → API Keys and click Create API Key.
- Name the key after the agent (for example Claude Code, intake team), choose the MCP preset, and create the key. The preset ticks every scope except
runs:write; untick any scope the agent does not need. - Copy the secret. It starts with
ds160_sk_and is shown only once. - Add the server to your client with the secret in an
Authorizationheader. In Claude Code:
claude mcp add --transport http ds160 https://www.ds160.io/api/mcp --header "Authorization: Bearer ds160_sk_…"
Other clients take the same address and header in their settings. In Cursor (.cursor/mcp.json):
{
"mcpServers": {
"ds160": {
"url": "https://www.ds160.io/api/mcp",
"headers": { "Authorization": "Bearer ds160_sk_…" }
}
}
}
In VS Code (.vscode/mcp.json), add the same url and headers under "servers" with "type": "http". Keep the secret out of files you commit: use your client’s support for environment variables or secret inputs where it has one.
Connect claude.ai or ChatGPT
claude.ai and ChatGPT do not send a key. They connect by signing in to DS160.io instead:
- In claude.ai or ChatGPT, add
https://www.ds160.io/api/mcpas a custom connector. - When it asks, sign in to DS160.io with your own account.
- Pick the business workspace the agent will work in.
- Review the permissions it asks for, then click to allow.
The agent then acts as you, in that workspace. You must be an Owner or Admin of the workspace to allow a connection.
Scopes: what each allows
| Scope | What the agent can do |
|---|---|
forms:read | List forms, read a form’s name and status, check what is missing, follow a run |
forms:write | Create forms (and read them) |
forms:clone | Copy an existing form into a new one |
client-links:write | Create client links to send to applicants |
answers:read | Read a form’s answers |
answers:write | Write and remove a form’s answers |
runs:write | Start a run at CEAC and follow it |
answers:read sends applicants’ personal data out of DS160.io. Names, dates of birth, passport numbers, travel history and every other DS-160 answer the agent reads are passed to the agent and to whoever runs it, such as an AI provider. Your workspace, as the controller of its clients’ data, decides whether that is allowed, and is responsible for that data once it leaves DS160.io (see the privacy policy and the Data Processing Agreement). If the agent only needs to fill forms in, leave answers:read out: with answers:write alone it can write answers without reading them back.
What the agent can and cannot do
- It can prepare a form: create it, fill in answers from what the client sent, see which questions are still missing or would be refused, and send the client a link for the rest.
- Every answer it writes is checked as the form editor checks it. Text is stored in capitals unless the question allows letters outside English. An answer that does not fit is refused with a reason, and nothing else in the batch is affected.
- Every change is recorded. The form’s Field history shows each change as “API key name” for a key, or as “member via agent” for a connection.
- It cannot sign or submit. A run started by an agent always stops before signing, whatever the workspace’s setting: a person checks the application and signs it on CEAC (see Review Before Signing).
- It cannot type CEAC’s captchas. A person types them in DS160.io while the run waits; the run tells the agent when someone is needed and where.
- Creating a form is billed like creating one in the app: each form created, or cloned, counts as a form created.
- It works only in its own workspace. It cannot see other workspaces, and a form from another workspace is reported as not found.
Tools
| Tool | What it does | Scope |
|---|---|---|
list_forms | Lists the workspace’s forms, newest first | forms:read |
get_form | Reads one form’s name, status and consent | forms:read |
create_form | Creates a form (billed per form created) | forms:write |
clone_form | Copies a form into a new one, optionally leaving sections out | forms:clone |
create_client_link | Creates a client link for a form | client-links:write |
search_docs | Searches these docs, 200 searches a day per key or connection | any |
describe_form_pages | Lists the form’s pages and each question: its answer path, type, whether it is required, dropdown values and the questions each answer opens, with the JSON for each type of answer | any |
get_answers | Reads a form’s answers, for the whole form or one page | answers:read |
set_answers | Writes up to 200 answers per call, in order: each is written, removed (value null) or refused with a code and reason | answers:write |
get_form_progress | For each section: its status, the required questions with no answer, and the problems by path; and whether the form is complete | forms:read, answers:read or answers:write |
start_run | Starts a run at CEAC for a chosen consulate; stops before signing | runs:write |
get_run | A run’s status, a summary of where it is, whether a person is needed, and the link to it | runs:write or forms:read |
Revoking access
- A key: revoke it under Workspace Settings → Team & access → API Keys. The next call the agent makes is refused.
- A connection (claude.ai, ChatGPT): revoke it in Account & Security, or for the whole workspace in Workspace Settings. Both list the connections that exist.
Revoke at once if a key may have leaked or an agent is no longer used.
Keep the agent to what the applicant gave
A DS-160 is signed by the applicant, who declares that every answer is true. Tell your agent to answer only from what the applicant gave you (their documents, emails and answers) and never to guess or make up an answer. Where it is not sure, it should leave the question empty, so that get_form_progress shows it as missing and the client or your staff answer it. Review the form before anyone signs it.
Troubleshooting
| Problem | What to do |
|---|---|
The client says it cannot connect, or gets 401 | Check the address is https://www.ds160.io/api/mcp and the header is Authorization: Bearer <secret>, with the whole secret. A revoked key cannot be used again: create a new one. |
| A tool says a scope is missing | The key or connection lacks that scope. Create a key with it (or connect again and allow it), then revoke the old one. |
| An answer is refused | Read its reason. Use describe_form_pages for the right path and the values a dropdown accepts. |
start_run is refused | get_form_progress lists what is missing or wrong in each section. Fill it in, then start the run again. If a run of the form is already going, follow it with get_run. |
The run waits with needsPerson | Someone opens the run’s link in DS160.io and types the captcha. Once the run is ready to sign, someone checks the application and signs it on CEAC. |