Intake application guides
Complete and submit client intake
Read as MarkdownComplete an existing client intake: save answers, attach documents, and submit it to the receiving Portal, or email the form to the client to finish. Start with the intake returned by business creation or an existing intake you can access.
Before you start: your key needs client_intake_applications:write with access to this intake. This permission covers reading, editing, and submitting. Emailing the form separately requires client_intake_sharing:write. Saving answers alone does not submit the intake.
Every request uses Authorization: Bearer <api-key>. JSON writes use Content-Type: application/json. Give each answer write, file upload, and submission its own Idempotency-Key; retain the exact request and key for retries. Examples use abbreviated responses and illustrative IDs. Use returned URLs and question keys from the live contract.
Read the application's detail URL to find the links for this workflow:
GET /v1/client-intake-applications/ciappl_example/
200 OK — relevant fields
{
"application": {
"id": "ciappl_example",
"status": { "key": "pre_submitted" },
"contract_url": "https://YOUR_API_HOST/v1/client-intake-applications/ciappl_example/contract/",
"answers_url": "https://YOUR_API_HOST/v1/client-intake-applications/ciappl_example/answers/",
"submission_readiness_url": "https://YOUR_API_HOST/v1/client-intake-applications/ciappl_example/submission-readiness/",
"submit_url": "https://YOUR_API_HOST/v1/client-intake-applications/ciappl_example/submit/"
}
}
1. Read the questions and save answers
Read the application’s contract_url. It describes this intake's questions, accepted values, and available operations.
GET /v1/client-intake-applications/ciappl_example/contract/
Response — example question, abbreviated
{
"questions": [
{
"block_key": "request",
"label": "Funding request",
"fields": [
{
"field_key": "amount",
"value_schema": { "type": "string" }
}
]
}
]
}
The keys above are examples, not standard fields present in every intake. Match your data to the actual questions and their value_schema.
Read the application's answers_url to obtain the current values and its ETag header. Suppose that header is ETag: "0". Save the amount using that exact ETag:
PATCH /v1/client-intake-applications/ciappl_example/answers/
If-Match: "0"
Idempotency-Key: website-lead-12345-amount-1
{
"updates": [
{ "block_key": "request", "field_key": "amount", "value": "250000" }
]
}
200 OK — relevant fields; response header ETag: "1"
{
"answers_revision": 1,
"updated_answers": [
{ "block_key": "request", "field_key": "amount", "value": "250000" }
]
}
When reading answers, pass a non-null next_cursor as the next request’s cursor query parameter until you have read every relevant page.
To correct the amount later, PATCH the same keys with the new value, the latest answers ETag, and a new idempotency key. Save only known facts. For repeated rows such as owners, POST to the contract's operations.repeatable_items_url with the group's block_key, the latest answers ETag, and a new idempotency key. Retain the returned item.item_id, then PATCH that URL with item_id and updates. Use the group and field keys from your contract.
2. Attach a supporting file
If the contract contains a file question, request an upload URL at its operations.file_uploads_url. For example, an intake might ask for a bank statement under the block key bank_statement:
POST /v1/client-intake-applications/ciappl_example/answers/files/
{ "block_key": "bank_statement" }
201 Created — relevant fields
{
"upload_url": "https://YOUR_API_HOST/v1/client-intake-applications/ciappl_example/answers/files/upl_example/",
"multipart_field": "file"
}
Send the file bytes to that upload_url as one multipart field named file. Use the current answers ETag, respect the returned expiry and size limit, and let your HTTP client generate the multipart boundary. The short command here illustrates the binary upload; the other examples use JSON.
curl -X POST '<upload_url>' \
-H 'Authorization: Bearer <api-key>' \
-H 'If-Match: "1"' \
-H 'Idempotency-Key: website-lead-12345-statement-1' \
-F 'file=@bank-statement.pdf'
201 Created — relevant fields; response header ETag: "2"
{ "answers_revision": 2 }
The upload attaches the file to its question. Do not add a file URL to the answer JSON. Read operations.files_url for attachment metadata; for a file inside a repeated row, include its item_id when requesting the upload capability.
After a connection failure, retry the original upload URL, file, key, and ETag to recover the result. Request a new upload URL only after a confirmed expiry rejection. On 412, read the latest answers and reconcile changes before starting a new write attempt.
3. Complete and submit the intake
Your integration can finish the intake, or the client can complete it in a branded web form. Both use the same saved answers and files.
Let the client finish
With client_intake_sharing:write, email the client a secure link to fill in the remaining answers, upload documents, and submit:
POST /v1/client-intake-applications/ciappl_example/share/
Idempotency-Key: website-lead-12345-share
{
"recipients": [{ "email": "maya@example.com" }],
"message": "Please complete and submit your intake form."
}
201 Created — relevant fields
{
"grants": [{ "email": "maya@example.com", "access_mode": "edit" }],
"delivery_state": "queued"
}
The request queues an email containing the link. These intake links grant editing access and expire after 30 days. queued does not confirm delivery. Follow the submission webhook below to know when the client finishes.
Retry the original request and idempotency key after a lost response. To resend access deliberately, use a new key: this refreshes the invitation and revokes the recipient's previous sessions, so they should use the newest email.
Submit through your integration
With client_intake_applications:write, complete the remaining questions, then GET the application's submission_readiness_url:
GET /v1/client-intake-applications/ciappl_example/submission-readiness/
Ready response — relevant fields
{
"has_blocking_issues": false,
"blocking_issue_count": 0,
"issues": [],
"submission_token": "<returned-submission-token>"
}
Readiness checks both the form and whether its current status permits submission. If there are blocking issues, submission_token is null. Correct the reported issues and check readiness again. A ready response supplies a token for the application and answers you just checked. Pass it unchanged to submit_url:
POST /v1/client-intake-applications/ciappl_example/submit/
Idempotency-Key: intake-example-submit-1
{ "submission_token": "<returned-submission-token>" }
200 OK — relevant fields
{
"application": {
"id": "ciappl_example",
"status": { "key": "submitted" },
"submitted_at": "2026-10-15T16:30:00Z"
}
}
If the application or answers change after the readiness check, submission returns 412. Review the changes, check readiness again, and send the new token with a new idempotency key. After a connection failure, retry the original token and key to recover the result. Submission uses the token instead of an If-Match header; answer edits still use ETags.
Submission validates the answers again, records the submission, and notifies the receiving team according to the Portal's notification settings. If the Portal has enabled stage synchronization and mapped a stage to submitted intakes, submission moves the business to that stage. It also starts automated intake preparation when applicable. That background work can continue after the submission response; a successful submit does not mean downstream preparation is finished.
In the receiving Portal, open Businesses → the business → Client intake to review the submitted form and attachments.
4. Follow subsequent updates
Subscribe to client_intake_application.submitted and client_intake_application.status_changed using the webhook setup guide. Set up the subscription before handing off or submitting the form if you need to capture that first event. Verify signatures, deduplicate deliveries, and reread the application to get its current state. Add client_intake_application.updated if you also need to follow saved answer changes before submission.
The Portal and shared form manage their own live websocket updates. Your API integration uses webhooks or REST polling; it does not need a websocket connection to follow the client's progress.
Without webhooks, poll the application detail URL at an appropriate interval. Your import is complete when the intended answers and files are saved and application.status.key is submitted; later review belongs to the receiving team.
If a step fails
| Response | Next action |
|---|---|
401, 403, or record not found | Check the environment, ID, key permissions, and source scope. Read-only access uses client_intake_applications:read; editing and submitting require client_intake_applications:write. |
412 | Reread the affected resource and reconcile concurrent changes. For submission, check readiness again after reviewing the latest application and answers. |
409 on a retry | Reuse a key only for the identical command, including its precondition. |
| Validation error | Correct the named field against the live contract; for submission, inspect readiness and complete the missing answers. |
Keep the application ID after any failure and resume the same intake.
