API & MCP Reference
Everything you need to integrate with Intake — via REST API or MCP.
Base URL:
https://dointake.com/api/v1
Introduction
Intake offers two integration paths: a REST API and an MCP server (Model Context Protocol). Both provide full access to clients, document requests, forms, and webhooks.
REST API Base URL: https://dointake.com/api/v1
MCP Server: Connect any MCP-compatible client (Claude, Cursor, etc.) — see the MCP Protocol section for setup.
All REST requests and responses use JSON. Timestamps are returned in ISO 8601 format. All resource IDs are opaque strings.
Plan limits:
| Free | Pro ($19/month) | |
|---|---|---|
| Clients | 10 | Unlimited |
| Deals | 3 | Unlimited |
| Parties per deal | 5 | Unlimited |
Authentication
Authenticate by including your API key in the Authorization header as a Bearer token.
API keys are created under Avatar menu → Settings → API Keys. Each key has scopes that control access:
| Scope | Access |
|---|---|
read | List and retrieve resources |
write | Create and update resources |
webhooks | Manage webhook subscriptions |
Rate limiting: 120 requests per minute per API key. When exceeded, the API returns 429 with a Retry-After header.
Test vs. Live Keys
API keys have a mode that controls data access:
| Key Prefix | Mode | Data Access |
|---|---|---|
dk_live_... | Live | Production data only |
dk_test_... | Test | Sandbox data only |
Test keys create and access isolated sandbox data — your production data is never affected. Test keys also bypass plan limits, so you can test freely without upgrading.
Create test keys in Settings → API Keys by selecting "Test" mode.
/api/v1/me
Test your API key
Returns information about the authenticated user.
curl https://dointake.com/api/v1/me \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": 1,
"name": "Your Name",
"email": "you@example.com",
"subscription_status": "active"
}
}
Errors
The API uses standard HTTP status codes. Errors return a JSON object with an error key containing code and message fields.
| Status | Meaning |
|---|---|
200 | Success |
201 | Created |
204 | Deleted (no content) |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — insufficient scope or plan limit exceeded |
404 | Not found |
422 | Validation error |
429 | Rate limited |
500 | Internal server error |
/api/v1/...
Error response format
All errors follow this structure. Validation errors include a `details` object mapping field names to error messages.
{
"error": {
"code": "validation_error",
"message": "Validation failed",
"details": {
"email": [
"can't be blank"
]
}
}
}
Pagination
List endpoints return all matching records. Use query parameters to filter results:
- Clients:
?search=termto filter by name or email;?external_id=x&external_source=yto match by external system;?tags=vip,residentialto filter by tags - Requests:
?status=pending&client_id=xxx&deal_id=xxx - Deals:
?status=active;?external_id=x&external_source=yto match by external system
Clients
Manage your clients. Clients are the people you collect documents from.
/api/v1/clients
List clients
Returns all clients for your account. Optionally filter by search term, external ID, or tags.
| Parameter | Type | Required | Description |
|---|---|---|---|
search |
string | optional | Filter by name or email |
external_id |
string | optional | Filter by external ID |
external_source |
string | optional | Filter by external source (used with external_id) |
tags |
string | optional | Filter by tags (comma-separated) |
curl https://dointake.com/api/v1/clients?search=jane \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "cl_abc123",
"name": "Jane Smith",
"metadata": {
"lead_score": 85
},
"email": "jane@example.com",
"updated_at": "2025-01-15T10:30:00Z",
"tags": [
"vip",
"residential"
],
"created_at": "2025-01-15T10:30:00Z",
"phone": "+1234567890",
"external_id": "contact_123",
"external_source": "salesforce"
}
]
}
/api/v1/clients/:id
Get a client
Retrieve a single client by their ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Client ID |
curl https://dointake.com/api/v1/clients/cl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "cl_abc123",
"name": "Jane Smith",
"metadata": {
"lead_score": 85
},
"email": "jane@example.com",
"updated_at": "2025-01-15T10:30:00Z",
"tags": [
"vip",
"residential"
],
"created_at": "2025-01-15T10:30:00Z",
"phone": "+1234567890",
"external_id": "contact_123",
"external_source": "salesforce"
}
}
/api/v1/clients
Create a client
Create a new client.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | required | Client's full name |
email |
string | required | Client's email address |
phone |
string | optional | Client's phone number |
external_id |
string | optional | External system ID |
external_source |
string | optional | External system name (e.g. salesforce, hubspot) |
metadata |
object | optional | Arbitrary key-value metadata |
tags |
array | optional | List of string tags |
curl -X POST https://dointake.com/api/v1/clients \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Jane Smith", "email": "jane@example.com"}'
{
"data": {
"id": "cl_abc123",
"name": "Jane Smith",
"metadata": {},
"email": "jane@example.com",
"updated_at": "2025-01-15T10:30:00Z",
"tags": [],
"created_at": "2025-01-15T10:30:00Z",
"phone": null,
"external_id": null,
"external_source": null
}
}
/api/v1/clients/:id
Update a client
Update a client's name, email, phone, or metadata.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Client ID |
name |
string | optional | Client's full name |
email |
string | optional | Client's email address |
phone |
string | optional | Client's phone number |
external_id |
string | optional | External system ID |
external_source |
string | optional | External system name |
metadata |
object | optional | Arbitrary key-value metadata |
tags |
array | optional | List of string tags |
curl -X PATCH https://dointake.com/api/v1/clients/cl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Jane Doe", "phone": "+1987654321"}'
{
"data": {
"id": "cl_abc123",
"name": "Jane Doe",
"metadata": {},
"email": "jane@example.com",
"updated_at": "2025-01-20T14:00:00Z",
"tags": [],
"created_at": "2025-01-15T10:30:00Z",
"phone": "+1987654321",
"external_id": null,
"external_source": null
}
}
/api/v1/clients/upsert
Upsert a client
Create or update a client by matching on `external_id` + `external_source`, or by `email` if no external ID is provided. Returns `201` if a new client was created, `200` if an existing client was updated.
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | required | Client's email address |
name |
string | optional | Client's full name |
phone |
string | optional | Client's phone number |
external_id |
string | optional | External system ID (used for matching) |
external_source |
string | optional | External system name (used with external_id for matching) |
metadata |
object | optional | Arbitrary key-value metadata |
tags |
array | optional | List of string tags |
curl -X PUT https://dointake.com/api/v1/clients/upsert \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com", "name": "Jane Smith", "external_id": "contact_123", "external_source": "salesforce"}'
{
"data": {
"id": "cl_abc123",
"name": "Jane Smith",
"metadata": {},
"email": "jane@example.com",
"updated_at": "2025-01-15T10:30:00Z",
"tags": [],
"created_at": "2025-01-15T10:30:00Z",
"phone": null,
"external_id": "contact_123",
"external_source": "salesforce"
}
}
Requests
Document requests are sent to clients to collect files and information. Each request has a checklist of items and collects uploads from the client.
/api/v1/requests
List requests
Returns all document requests. Filter by status, client, or deal.
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
string | optional | Filter by status (e.g. pending, sent, complete) |
client_id |
string | optional | Filter by client ID |
deal_id |
string | optional | Filter by deal ID |
curl "https://dointake.com/api/v1/requests?status=sent" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "req_xyz789",
"status": "sent",
"title": "Tax Documents 2025",
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"sent_at": "2025-01-20T09:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"due_date": "2025-04-15",
"item_count": 5
}
]
}
/api/v1/requests/:id
Get a request
Retrieve a single request with its checklist items.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "req_xyz789",
"status": "sent",
"description": "Please upload your tax documents",
"title": "Tax Documents 2025",
"forms": [
{
"id": "sub_abc123",
"name": "Client Intake Form",
"status": "completed",
"completed_at": "2025-01-20T14:30:00Z",
"submitted_ip": "203.0.113.42",
"responses": [
{
"label": "Full Name",
"type": "text",
"value": "Jane Smith",
"field_id": "field_1"
},
{
"label": "Signature",
"signed": true,
"type": "signature",
"value": null,
"field_id": "field_2"
}
]
}
],
"items": [
{
"id": "item_001",
"name": "W-2 Form",
"position": 0,
"status": "pending",
"description": "From your employer",
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"upload_count": 0
}
],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"sent_at": "2025-01-20T09:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"due_date": "2025-04-15",
"deal_id": null,
"max_reminders": 10,
"notify_professional_after_days": 7,
"reminder_count": 3,
"item_count": 1,
"last_reminder_at": "2025-02-10T09:00:00Z",
"next_reminder_at": "2025-02-12",
"pdf_forms": [
{
"id": "pfr_abc123",
"status": "completed",
"completed_at": "2025-01-20T15:00:00Z",
"template_name": "Engagement Letter",
"download_url": "https://dointake.com/downloads/tok_xyz789"
}
]
}
}
/api/v1/requests
Create a request
Create a new document request for a client. Optionally include checklist items.
| Parameter | Type | Required | Description |
|---|---|---|---|
client_id |
string | required | Client ID to send the request to |
title |
string | required | Request title |
description |
string | optional | Description or instructions |
due_date |
string | optional | Due date (YYYY-MM-DD) |
items |
array | optional | Array of checklist items to create |
curl -X POST https://dointake.com/api/v1/requests \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"client_id": "cl_abc123", "title": "Tax Documents", "items": [{"name": "W-2 Form"}]}'
{
"data": {
"id": "req_xyz789",
"status": "pending",
"description": null,
"title": "Tax Documents",
"items": [
{
"id": "item_001",
"name": "W-2 Form",
"position": 0,
"status": "pending",
"description": null,
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"upload_count": 0
}
],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"sent_at": null,
"created_at": "2025-01-15T10:30:00Z",
"due_date": null,
"deal_id": null,
"item_count": 1
}
}
/api/v1/requests/:id
Update a request
Update an existing document request.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Request ID |
title |
string | optional | Request title |
description |
string | optional | Description or instructions |
due_date |
string | optional | Due date (YYYY-MM-DD) |
reminder_enabled |
boolean | optional | Enable email reminders |
reminder_frequency_days |
integer | optional | Days between reminders (1–30) |
max_reminders |
integer | optional | Max reminders to send (1–100, null for unlimited) |
notify_professional_after_days |
integer | optional | Notify professional if no response after N days (1–90, null to disable) |
curl -X PATCH https://dointake.com/api/v1/requests/req_xyz789 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Updated Title"}'
{
"data": {
"id": "req_xyz789",
"status": "sent",
"description": null,
"title": "Updated Title",
"items": [],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"sent_at": "2025-01-20T09:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"due_date": "2025-04-15",
"deal_id": null,
"item_count": 0
}
}
/api/v1/requests/:id/send
Send a request
Send a draft request to the client via email. Changes status from draft to sent. Set `skip_email` to activate the request without emailing the client (useful for sharing the portal link manually).
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Request ID |
skip_email |
boolean | optional | If true, activate the request without sending an email to the client. Default: false. |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/send \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "req_xyz789",
"status": "sent",
"title": "Tax Documents",
"items": [],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"sent_at": "2025-01-20T09:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"deal_id": null,
"item_count": 0
}
}
/api/v1/requests/:id/complete
Complete a request
Mark a document request as complete.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Request ID |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/complete \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "req_xyz789",
"status": "complete",
"title": "Tax Documents",
"items": [],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": "2025-02-01T14:00:00Z",
"created_at": "2025-01-15T10:30:00Z",
"deal_id": null,
"item_count": 0
}
}
/api/v1/requests/:id/reopen
Reopen a request
Reopen a completed document request.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Request ID |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/reopen \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "req_xyz789",
"status": "sent",
"title": "Tax Documents",
"items": [],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"completed_at": null,
"created_at": "2025-01-15T10:30:00Z",
"deal_id": null,
"item_count": 0
}
}
/api/v1/requests/:request_id/reminders
List reminders
Returns the reminder audit trail for a request. Shows when each reminder was sent, to whom, and which items were pending.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789/reminders \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"sent_at": "2025-02-01T09:00:00Z",
"urgency": "friendly",
"pending_items": [
"W-2 Form",
"1099 Form"
],
"recipient_email": "jane@example.com",
"recipient_type": "client"
},
{
"sent_at": "2025-02-04T09:00:00Z",
"urgency": "urgent",
"pending_items": [
"W-2 Form"
],
"recipient_email": "jane@example.com",
"recipient_type": "client"
},
{
"sent_at": "2025-02-08T09:00:00Z",
"urgency": "overdue",
"pending_items": [],
"recipient_email": "pro@example.com",
"recipient_type": "professional"
}
]
}
Checklist Items
Checklist items are individual documents or pieces of information requested within a document request.
/api/v1/requests/:request_id/items
List checklist items
Returns all checklist items for a given request.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789/items \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "item_001",
"name": "W-2 Form",
"position": 0,
"status": "pending",
"description": "From your employer",
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"review_note": null,
"review_status": null,
"upload_count": 0
}
]
}
/api/v1/requests/:request_id/items
Create a checklist item
Add a new checklist item to a request.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
name |
string | required | Item name |
description |
string | optional | Instructions for the client |
required |
boolean | optional | Whether this item is required (default: true) |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/items \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "W-2 Form", "description": "From your employer"}'
{
"data": {
"id": "item_002",
"name": "W-2 Form",
"position": 1,
"status": "pending",
"description": "From your employer",
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"review_note": null,
"review_status": null,
"upload_count": 0
}
}
/api/v1/items/:id
Update item review status
Set the review status and optional note on a checklist item. Only works when the item's status is "received". Review status values are validated against the user's configured list (defaults: in_review, approved, rejected).
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Item ID |
review_status |
string | required | Review status (user-configured, defaults: in_review, approved, rejected) |
review_note |
string | optional | Optional note (e.g. reason for rejection) |
curl -X PATCH https://dointake.com/api/v1/items/itm_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"review_status": "in_review"}'
{
"data": {
"id": "itm_abc123",
"name": "W-2 Form",
"position": 0,
"status": "received",
"description": "From your employer",
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"review_note": null,
"review_status": "in_review",
"upload_count": 1
}
}
/api/v1/items/:id/re-request
Re-request item
Re-request a checklist item. Resets status to pending, clears review_status and review_note. Previous uploads are preserved for reference.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Item ID |
curl -X POST https://dointake.com/api/v1/items/itm_abc123/re-request \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "itm_abc123",
"name": "W-2 Form",
"position": 0,
"status": "pending",
"description": "From your employer",
"required": true,
"created_at": "2025-01-15T10:30:00Z",
"review_note": null,
"review_status": null,
"upload_count": 1
}
}
Uploads
Uploads are files submitted by clients in response to document requests. Uploads are read-only via the API.
/api/v1/requests/:request_id/uploads
List uploads
Returns all uploads for a given request.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789/uploads \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "upl_001",
"status": "processed",
"file_size": 245000,
"content_type": "application/pdf",
"created_at": "2025-01-20T14:30:00Z",
"original_filename": "w2-2025.pdf",
"ai_classification": "W-2",
"ai_confidence": 0.97,
"submitted_ip": "203.0.113.42",
"item_id": "item_001"
}
]
}
Form Templates
Form templates define reusable sets of fields that can be attached to requests. When attached, the template's fields are snapshotted so future edits don't affect existing requests.
/api/v1/form-templates
List form templates
Returns all form templates for your account.
curl https://dointake.com/api/v1/form-templates \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "tmpl_abc123",
"name": "Client Intake",
"description": "Basic client information form",
"updated_at": "2025-01-15T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z",
"field_count": 5
}
]
}
/api/v1/form-templates/:id
Get a form template
Retrieve a form template with all its field definitions.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Template ID |
curl https://dointake.com/api/v1/form-templates/tmpl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "tmpl_abc123",
"name": "Client Intake",
"description": "Basic client information form",
"fields": [
{
"id": 1,
"label": "Full Legal Name",
"position": 0,
"type": "text",
"config": {
"validation": "none"
},
"description": null,
"required": true
},
{
"id": 2,
"label": "Email Address",
"position": 1,
"type": "email",
"config": {
"validation": "email"
},
"description": null,
"required": true
},
{
"id": 3,
"label": "Filing Status",
"position": 2,
"type": "select",
"config": {
"options": [
"Single",
"Married",
"Head of Household"
]
},
"description": "Select your tax filing status",
"required": true
}
],
"updated_at": "2025-01-15T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z"
}
}
/api/v1/form-templates
Create a form template
Create a new form template, optionally with fields in one call. Field types: text, textarea, number, date, email, phone, select, radio, checkbox, signature. Fields with options (select, radio, checkbox) need config.options.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | required | Template name |
description |
string | optional | Template description |
fields |
array | optional | List of field objects with type, label, required, description, config |
curl -X POST https://dointake.com/api/v1/form-templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Client Intake", "fields": [{"type": "text", "label": "Full Name", "required": true}, {"type": "email", "label": "Email", "config": {"validation": "email"}}]}'
{
"data": {
"id": "tmpl_abc123",
"name": "Client Intake",
"description": null,
"fields": [
{
"id": 1,
"label": "Full Name",
"position": 0,
"type": "text",
"config": {
"validation": "none"
},
"description": null,
"required": true
},
{
"id": 2,
"label": "Email",
"position": 1,
"type": "email",
"config": {
"validation": "email"
},
"description": null,
"required": false
}
],
"updated_at": "2025-01-15T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z"
}
}
/api/v1/form-templates/:id
Delete a form template
Delete a form template. Returns 409 if the template is attached to any requests.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Template ID |
curl -X DELETE https://dointake.com/api/v1/form-templates/tmpl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
204 No Content
/api/v1/form-templates/:form_template_id/fields
Add a field
Add a field to an existing form template. The field is appended to the end.
| Parameter | Type | Required | Description |
|---|---|---|---|
form_template_id |
string | required | Template ID |
type |
string | required | Field type: text, textarea, number, date, email, phone, select, radio, checkbox, signature |
label |
string | required | Field label |
description |
string | optional | Help text |
required |
boolean | optional | Whether field is required (default: false) |
config |
object | optional | Field config — options for select/radio/checkbox, validation rule |
curl -X POST https://dointake.com/api/v1/form-templates/tmpl_abc123/fields \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "select", "label": "State", "config": {"options": ["CA", "NY", "TX"]}}'
{
"data": {
"id": 4,
"label": "State",
"position": 3,
"type": "select",
"config": {
"options": [
"CA",
"NY",
"TX"
]
},
"description": null,
"required": false
}
}
/api/v1/form-templates/:form_template_id/fields/:id
Update a field
Update a field on a form template.
| Parameter | Type | Required | Description |
|---|---|---|---|
form_template_id |
string | required | Template ID |
id |
integer | required | Field ID |
label |
string | optional | Updated label |
required |
boolean | optional | Updated required flag |
config |
object | optional | Updated config |
curl -X PATCH https://dointake.com/api/v1/form-templates/tmpl_abc123/fields/4 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "Updated Label", "required": true}'
{
"data": {
"id": 4,
"label": "Updated Label",
"position": 3,
"type": "select",
"config": {
"options": [
"CA",
"NY",
"TX"
]
},
"description": null,
"required": true
}
}
/api/v1/form-templates/:form_template_id/fields/:id
Delete a field
Remove a field from a form template.
| Parameter | Type | Required | Description |
|---|---|---|---|
form_template_id |
string | required | Template ID |
id |
integer | required | Field ID |
curl -X DELETE https://dointake.com/api/v1/form-templates/tmpl_abc123/fields/4 \
-H "Authorization: Bearer YOUR_API_KEY"
204 No Content
Request Forms
Attach form templates or inline forms to requests. Clients fill them out in the portal alongside document uploads.
/api/v1/requests/:request_id/forms
Attach a form
Attach a saved template or create an inline form on a request. To attach a template, send template_id. For an inline form, send name and fields.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
template_id |
string | optional | Template ID (for saved templates) |
name |
string | optional | Form name (for inline forms) |
fields |
array | optional | Field definitions (for inline forms) |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/forms \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"template_id": "tmpl_abc123"}'
{
"data": {
"id": "sub_def456",
"fields": [
{
"id": "1",
"label": "Full Name",
"position": 0,
"type": "text",
"config": {},
"required": true
},
{
"id": "2",
"label": "Email",
"position": 1,
"type": "email",
"config": {
"validation": "email"
},
"required": true
}
],
"completed": false,
"completed_at": null,
"template_name": "Client Intake",
"created_at": "2025-01-20T09:00:00Z"
}
}
/api/v1/requests/:request_id/forms
List attached forms
List all forms attached to a request. Completed forms include responses.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789/forms \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "sub_def456",
"fields": [
{
"id": "1",
"label": "Full Name",
"position": 0,
"type": "text",
"config": {},
"required": true
}
],
"completed": true,
"completed_at": "2025-01-21T15:00:00Z",
"template_name": "Client Intake",
"created_at": "2025-01-20T09:00:00Z",
"responses": [
{
"label": "Full Name",
"type": "text",
"value": "Jane Smith",
"field_id": "1"
}
]
}
]
}
/api/v1/requests/:request_id/forms/:id
Detach a form
Remove an attached form from a request. Also deletes any responses.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
id |
string | required | Request form ID (sub_xxx) |
curl -X DELETE https://dointake.com/api/v1/requests/req_xyz789/forms/sub_def456 \
-H "Authorization: Bearer YOUR_API_KEY"
204 No Content
PDF Templates
Upload and manage form-fillable PDF templates. Uploaded PDFs are analyzed for native AcroForm fields. You can also place custom fields via the editor UI.
/api/v1/pdf-templates
List PDF templates
List all PDF templates for the authenticated user.
curl https://dointake.com/api/v1/pdf-templates \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "ptl_abc123",
"name": "W-9 Form",
"filename": "w9.pdf",
"file_size": 85432,
"created_at": "2025-02-01T10:00:00Z",
"page_count": 1,
"has_native_fields": true,
"custom_field_count": 0,
"native_field_count": 12
}
]
}
/api/v1/pdf-templates/:id
Get a PDF template
Get a PDF template with field definitions and an editor URL for the placement UI.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Template ID (ptl_xxx) |
curl https://dointake.com/api/v1/pdf-templates/ptl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "ptl_abc123",
"name": "W-9 Form",
"filename": "w9.pdf",
"file_size": 85432,
"content_type": "application/pdf",
"created_at": "2025-02-01T10:00:00Z",
"page_count": 1,
"has_native_fields": true,
"native_field_definitions": [
{
"name": "topmostSubform[0].Page1[0].f1_1[0]",
"type": "Text"
}
],
"custom_field_definitions": [],
"editor_url": "https://dointake.com/dashboard/pdf-templates/ptl_abc123"
}
}
/api/v1/pdf-templates
Upload a PDF template
Upload a PDF file as a new template. Use multipart/form-data with a 'file' field. The PDF is analyzed for page count and native form fields.
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
file | required | PDF file (multipart upload) |
curl -X POST https://dointake.com/api/v1/pdf-templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@w9.pdf"
{
"data": {
"id": "ptl_def456",
"name": "w9",
"filename": "w9.pdf",
"file_size": 85432,
"content_type": "application/pdf",
"created_at": "2025-02-01T10:00:00Z",
"page_count": 1,
"has_native_fields": true,
"native_field_definitions": [
{
"name": "topmostSubform[0].Page1[0].f1_1[0]",
"type": "Text"
}
],
"custom_field_definitions": [],
"editor_url": "https://dointake.com/dashboard/pdf-templates/ptl_def456"
}
}
/api/v1/pdf-templates/:id
Delete a PDF template
Delete a PDF template. Returns 409 if the template is attached to any requests.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Template ID (ptl_xxx) |
curl -X DELETE https://dointake.com/api/v1/pdf-templates/ptl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
204 No Content
PDF Fill Requests
Attach PDF templates to requests as fill requests. Clients fill in the PDF fields in the portal and the completed PDF is stored as an upload.
/api/v1/requests/:request_id/pdf-fills
Attach a PDF fill request
Attach a PDF template to a request. The client will be prompted to fill in the PDF fields.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
pdf_template_id |
string | required | PDF template ID (ptl_xxx) |
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/pdf-fills \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pdf_template_id": "ptl_abc123"}'
{
"data": {
"id": "pfr_ghi789",
"status": "pending",
"completed_at": null,
"template_id": "ptl_abc123",
"template_name": "W-9 Form",
"created_at": "2025-02-01T12:00:00Z",
"has_native_fields": true
}
}
/api/v1/requests/:request_id/pdf-fills
List PDF fill requests
List all PDF fill requests for a given request.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
curl https://dointake.com/api/v1/requests/req_xyz789/pdf-fills \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "pfr_ghi789",
"status": "completed",
"completed_at": "2025-02-02T09:30:00Z",
"template_id": "ptl_abc123",
"template_name": "W-9 Form",
"created_at": "2025-02-01T12:00:00Z",
"has_native_fields": true
}
]
}
/api/v1/requests/:request_id/pdf-fills/:id
Delete a PDF fill request
Remove a PDF fill request from a request.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id |
string | required | Request ID |
id |
string | required | Fill request ID (pfr_xxx) |
curl -X DELETE https://dointake.com/api/v1/requests/req_xyz789/pdf-fills/pfr_ghi789 \
-H "Authorization: Bearer YOUR_API_KEY"
204 No Content
Deals
Deals represent multi-party transactions (e.g. real estate closings, loan applications). Each deal has parties, and each party can have associated document requests.
/api/v1/deals
List deals
Returns all deals. Optionally filter by status, external ID, or external source.
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
string | optional | Filter by status (e.g. active, closed) |
external_id |
string | optional | Filter by external ID |
external_source |
string | optional | Filter by external source (used with external_id) |
curl https://dointake.com/api/v1/deals \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "deal_001",
"name": "123 Main St Closing",
"status": "active",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": "opp_456",
"external_source": "salesforce",
"property_address": "123 Main St, Springfield, IL 62701",
"party_count": 3
}
]
}
/api/v1/deals/:id
Get a deal
Retrieve a deal with all parties and their requests.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Deal ID |
curl https://dointake.com/api/v1/deals/deal_001 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "deal_001",
"name": "123 Main St Closing",
"status": "active",
"description": "Residential purchase",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": "opp_456",
"external_source": "salesforce",
"property_address": "123 Main St, Springfield, IL 62701",
"parties": [
{
"id": "pty_001",
"position": 0,
"requests": [
{
"id": "req_xyz789",
"status": "sent",
"title": "Buyer Documents",
"item_count": 5
}
],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"role": "Buyer",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [],
"delegate_to_id": null
}
],
"party_count": 1
}
}
/api/v1/deals
Create a deal
Create a new deal. Optionally create from a deal template by passing `template_id`.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | required | Deal name |
description |
string | optional | Deal description |
template_id |
string | optional | Create from a deal template |
external_id |
string | optional | External system ID |
external_source |
string | optional | External system name |
metadata |
object | optional | Arbitrary key-value metadata |
property_address |
string | optional | Property address for the deal |
curl -X POST https://dointake.com/api/v1/deals \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "123 Main St Closing"}'
{
"data": {
"id": "deal_002",
"name": "123 Main St Closing",
"status": "active",
"description": null,
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"property_address": null,
"parties": [],
"party_count": 0
}
}
/api/v1/deals/:id
Update a deal
Update an existing deal.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Deal ID |
name |
string | optional | Deal name |
description |
string | optional | Deal description |
external_id |
string | optional | External system ID |
external_source |
string | optional | External system name |
metadata |
object | optional | Arbitrary key-value metadata |
property_address |
string | optional | Property address for the deal |
curl -X PATCH https://dointake.com/api/v1/deals/deal_001 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "456 Oak Ave Closing"}'
{
"data": {
"id": "deal_001",
"name": "456 Oak Ave Closing",
"status": "active",
"description": "Residential purchase",
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"property_address": null,
"parties": [],
"party_count": 1
}
}
/api/v1/deals/upsert
Upsert a deal
Create or update a deal by matching on `external_id` + `external_source`. Returns `201` if a new deal was created, `200` if an existing deal was updated.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | required | Deal name |
description |
string | optional | Deal description |
external_id |
string | required | External system ID (used for matching) |
external_source |
string | required | External system name (used with external_id for matching) |
metadata |
object | optional | Arbitrary key-value metadata |
property_address |
string | optional | Property address for the deal |
curl -X PUT https://dointake.com/api/v1/deals/upsert \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "123 Main St Closing", "external_id": "opp_456", "external_source": "salesforce", "property_address": "123 Main St, Springfield, IL 62701"}'
{
"data": {
"id": "deal_002",
"name": "123 Main St Closing",
"status": "active",
"description": null,
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": "opp_456",
"external_source": "salesforce",
"property_address": "123 Main St, Springfield, IL 62701",
"parties": [],
"party_count": 0
}
}
/api/v1/deals/dispatch
Dispatch a deal
One-call deal dispatch. Creates a deal from a template, assigns clients to parties (creating clients as needed), creates requests, and optionally sends them. Clients are matched by `external_id` + `external_source` or by `email`.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id |
string | required | Deal template ID (dtpl_xxx) |
name |
string | required | Deal name |
property_address |
string | optional | Property address |
external_id |
string | optional | External system ID for the deal |
external_source |
string | optional | External system name for the deal |
auto_send |
boolean | optional | If true, automatically send all requests to clients (default: false) |
parties |
array | required | Array of party objects with role and client info: {role, client: {email, name, external_id, external_source}} |
curl -X POST https://dointake.com/api/v1/deals/dispatch \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"template_id": "dtpl_abc123", "name": "Smith Purchase", "auto_send": true, "parties": [{"role": "Borrower", "client": {"email": "jane@example.com", "name": "Jane Smith"}}]}'
{
"data": {
"id": "deal_xyz789",
"name": "Smith Purchase",
"status": "active",
"description": null,
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"property_address": null,
"parties": [
{
"id": "pty_001",
"position": 0,
"requests": [
{
"id": "req_abc789",
"status": "sent",
"title": "Borrower — Documents",
"item_count": 3
}
],
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"role": "Borrower",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [],
"delegate_to_id": null
}
],
"party_count": 1
}
}
Deal Templates
Deal templates define reusable multi-party workflows. Each template has party roles with pre-configured form templates, PDF templates, and document checklists. Create a deal from a template to get all parties set up in one call.
/api/v1/deal-templates
List deal templates
Returns all deal templates available to the user, including system templates.
curl https://dointake.com/api/v1/deal-templates \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "dtpl_abc123",
"name": "Mortgage Loan",
"description": "Standard mortgage loan closing workflow",
"created_at": "2025-01-15T10:30:00Z",
"is_system": true,
"party_count": 10
}
]
}
/api/v1/deal-templates/:id
Get a deal template
Retrieve a deal template with all party roles and their attached form templates, PDF templates, and document types.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Deal template ID |
curl https://dointake.com/api/v1/deal-templates/dtpl_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "dtpl_abc123",
"name": "Mortgage Loan",
"description": "Standard mortgage loan closing workflow",
"created_at": "2025-01-15T10:30:00Z",
"parties": [
{
"position": 0,
"role": "Borrower",
"document_types": [
{
"id": 1,
"name": "Pay Stub"
}
],
"form_templates": [
{
"id": "ftpl_abc123",
"name": "Personal Info"
}
],
"pdf_templates": [
{
"id": "ptl_abc123",
"name": "W-9 Form"
}
]
},
{
"position": 1,
"role": "Escrow",
"document_types": [],
"form_templates": [],
"pdf_templates": []
}
],
"is_system": true,
"party_count": 2
}
}
/api/v1/deals
Create deal from template
Create a deal from a template by passing `template_id`. All party roles from the template are created automatically. Assign clients to parties afterward.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | required | Deal name |
template_id |
string | required | Deal template ID (dtpl_xxx) |
description |
string | optional | Deal description |
curl -X POST https://dointake.com/api/v1/deals \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Smith Purchase", "template_id": "dtpl_abc123"}'
{
"data": {
"id": "deal_xyz789",
"name": "Smith Purchase",
"status": "draft",
"description": null,
"metadata": {},
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"property_address": null,
"parties": [
{
"id": "pty_001",
"position": 0,
"requests": [],
"client": null,
"role": "Borrower",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [],
"delegate_to_id": null
},
{
"id": "pty_002",
"position": 1,
"requests": [],
"client": null,
"role": "Escrow",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [],
"delegate_to_id": null
}
],
"party_count": 2
}
}
/api/v1/deals/:deal_id/parties/:party_id/requests/from-template
Create request from template
Create a fully-loaded request for a party using the deal template's attachments. Automatically attaches all form templates, PDF templates, and document checklists defined for this party's role. The party must have a client assigned.
| Parameter | Type | Required | Description |
|---|---|---|---|
deal_id |
string | required | Deal ID |
party_id |
string | required | Party ID |
curl -X POST https://dointake.com/api/v1/deals/deal_xyz789/parties/pty_001/requests/from-template \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": {
"id": "req_abc789",
"status": "draft",
"title": "Borrower — Documents",
"items": [
{
"id": "item_001",
"name": "Pay Stub",
"status": "pending"
}
],
"client": {
"id": "cli_abc123",
"name": "Jane Smith",
"email": "borrower@example.com"
},
"created_at": "2025-01-15T10:30:00Z"
}
}
Parties
Parties represent participants in a deal (e.g. buyer, seller, lender). Each party can be linked to a client.
/api/v1/deals/:deal_id/parties
List parties
Returns all parties for a deal.
| Parameter | Type | Required | Description |
|---|---|---|---|
deal_id |
string | required | Deal ID |
curl https://dointake.com/api/v1/deals/deal_001/parties \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "pty_001",
"position": 0,
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"role": "Buyer",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [
"assistant@example.com"
],
"delegate_to_id": null
}
]
}
/api/v1/deals/:deal_id/parties
Create a party
Add a party to a deal. Optionally link to an existing client.
| Parameter | Type | Required | Description |
|---|---|---|---|
deal_id |
string | required | Deal ID |
role |
string | required | Party role (e.g. Buyer, Seller) |
client_id |
string | optional | Link to an existing client |
cc_emails |
array of strings | optional | CC email addresses for notifications (max 10) |
delegate_to_id |
string | optional | Party ID to delegate requests to. The delegate's client receives emails instead. Must be in the same deal. Set to null to clear. |
external_id |
string | optional | External system ID for this party |
external_source |
string | optional | External system name (e.g. follow_up_boss) |
curl -X POST https://dointake.com/api/v1/deals/deal_001/parties \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"role": "Seller", "client_id": "cl_def456", "cc_emails": ["assistant@example.com"]}'
{
"data": {
"id": "pty_002",
"position": 1,
"client": {
"id": "cl_def456",
"name": "Bob Jones",
"email": "bob@example.com"
},
"role": "Seller",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [
"assistant@example.com"
],
"delegate_to_id": null
}
}
/api/v1/parties/:id
Update a party
Update a party's role or linked client.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Party ID |
role |
string | optional | Party role |
client_id |
string | optional | Link to a different client |
cc_emails |
array of strings | optional | CC email addresses for notifications (max 10) |
delegate_to_id |
string | optional | Party ID to delegate requests to. The delegate's client receives emails instead. Must be in the same deal. Set to null to clear. |
external_id |
string | optional | External system ID for this party |
external_source |
string | optional | External system name (e.g. follow_up_boss) |
curl -X PATCH https://dointake.com/api/v1/parties/pty_001 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"delegate_to_id": "pty_002"}'
{
"data": {
"id": "pty_001",
"position": 0,
"client": {
"id": "cl_abc123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"role": "Primary Buyer",
"created_at": "2025-01-15T10:30:00Z",
"external_id": null,
"external_source": null,
"cc_emails": [
"assistant@example.com",
"team@example.com"
],
"delegate_to_id": "pty_002"
}
}
Activity Log
An immutable audit trail of events in your account. Use this endpoint to track changes to deals, requests, parties, and documents.
/api/v1/activity
List activity
Returns activity log entries for your account, newest first. Supports cursor-based pagination and filtering.
| Parameter | Type | Required | Description |
|---|---|---|---|
deal_id |
string | optional | Filter by deal public ID |
party_id |
string | optional | Filter by party public ID |
request_id |
string | optional | Filter by request public ID |
resource_type |
string | optional | Filter by resource type (deal, request, party, item, upload) |
event_type |
string | optional | Filter by event type (e.g. request.sent, deal.created) |
before |
string | optional | Cursor for pagination — pass an activity ID to fetch older entries |
limit |
integer | optional | Max entries to return (1–100, default 50) |
curl https://dointake.com/api/v1/activity?deal_id=deal_abc123 \
-H "Authorization: Bearer sk_live_..."
{
"data": [
{
"id": "act_x1y2z3a4b5",
"metadata": {
"client_name": "Jane Doe",
"request_title": "Tax Documents 2025"
},
"request_id": "req_abc123",
"deal_id": "deal_abc123",
"event_type": "request.sent",
"resource_id": "req_abc123",
"resource_type": "request",
"party_id": "pty_def456",
"occurred_at": "2025-03-15T10:30:00Z"
}
]
}
Webhooks
Subscribe to events in your account. When an event occurs, we send an HTTP POST to your configured URL with a JSON payload.
/api/v1/webhooks
List webhooks
Returns all webhook subscriptions.
curl https://dointake.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"active": true,
"id": "wh_001",
"events": [
"request.completed",
"upload.created"
],
"description": "Production webhook",
"url": "https://example.com/webhook",
"created_at": "2025-01-15T10:30:00Z"
}
]
}
/api/v1/webhooks
Create a webhook
Create a new webhook subscription. The response includes a `secret` for verifying webhook signatures — store it securely, it won't be shown again.
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
string | required | URL to receive webhook events |
events |
array | required | Event types to subscribe to |
description |
string | optional | Description for this webhook |
active |
boolean | optional | Whether the webhook is active (default: true) |
curl -X POST https://dointake.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/webhook", "events": ["request.completed"]}'
{
"data": {
"active": true,
"id": "wh_002",
"events": [
"request.completed"
],
"description": null,
"secret": "whsec_abc123...",
"url": "https://example.com/webhook",
"created_at": "2025-01-15T10:30:00Z"
}
}
/api/v1/webhooks/:id
Update a webhook
Update a webhook subscription.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Webhook ID |
url |
string | optional | URL to receive webhook events |
events |
array | optional | Event types to subscribe to |
description |
string | optional | Description |
active |
boolean | optional | Whether the webhook is active |
curl -X PATCH https://dointake.com/api/v1/webhooks/wh_001 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"active": false}'
{
"data": {
"active": false,
"id": "wh_001",
"events": [
"request.completed",
"upload.created"
],
"description": "Production webhook",
"url": "https://example.com/webhook",
"created_at": "2025-01-15T10:30:00Z"
}
}
/api/v1/webhooks/:id
Delete a webhook
Permanently delete a webhook subscription.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | required | Webhook ID |
curl -X DELETE https://dointake.com/api/v1/webhooks/wh_001 \
-H "Authorization: Bearer YOUR_API_KEY"
# 204 No Content
Webhook Events
When an event occurs, we send a POST request to your webhook URL with a JSON payload.
Available event types:
| Event | Fired when |
|---|---|
client.created | A new client is created |
client.updated | A client is updated |
request.created | A new document request is created |
request.completed | A request is marked complete |
request.reopened | A completed request is reopened |
request.overdue | A request is overdue |
upload.created | A client uploads a file |
pdf_form.completed | A client completes a fillable PDF form |
deal.created | A new deal is created |
deal.updated | A deal is updated |
deal.status_changed | A deal's status changes |
Payload format:
{
"event": "request.completed",
"mode": "live",
"data": { ... },
"created_at": "2025-01-20T14:00:00Z"
}
The data field contains the full resource object (same shape as the corresponding GET endpoint).
The mode field is "test" for events from sandbox data, "live" for production data. Use this to filter or route test events in your automation platform.
Webhook Security
Every webhook delivery includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret.
Always verify signatures to ensure payloads are from Intake and haven't been tampered with.
Verification — Node.js
Verify in Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Verification — Python
Verify in Python
import hmac, hashlib
def verify_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Verification — Ruby
Verify in Ruby
require 'openssl'
def verify_webhook(payload, signature, secret)
expected = OpenSSL::HMAC.hexdigest(
'sha256', secret, payload
)
Rack::Utils.secure_compare(signature, expected)
end
Verification — PHP
Verify in PHP
function verifyWebhook($payload, $signature, $secret) {
$expected = hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}
MCP Protocol
Intake exposes a native Model Context Protocol (MCP) server, letting AI agents like Claude, GPT, or custom agents manage your entire intake workflow programmatically.
Endpoint: POST https://dointake.com/mcp
Protocol: JSON-RPC 2.0 (MCP spec 2025-03-26)
Authentication: OAuth 2.1 (recommended) or API key Bearer token. Tool access respects read / write scopes.
Connecting as a Claude Connector (Recommended)
Intake is available as a native Claude Connector. This is the easiest way to connect — no API key or config files needed.
In Claude Desktop or claude.ai:
- Open Settings → Connectors
- Click Add Connector
- Enter the URL:
https://dointake.com/mcp - Claude will automatically discover the authorization server, open a consent screen, and connect to your Intake account
That's it. Claude handles authorization and token management automatically.
Connecting with an API Key
For programmatic agents, CI/CD, or clients that don't support OAuth, you can use an API key instead.
Claude Desktop — Add to your claude_desktop_config.json:
{
"mcpServers": {
"intake": {
"url": "https://dointake.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
Cursor — Add to your .cursor/mcp.json:
{
"mcpServers": {
"intake": {
"url": "https://dointake.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
Protocol Flow
A typical MCP session follows three steps:
1. Initialize — Negotiate protocol version and discover server capabilities.
→ {"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": "2025-03-26",
"clientInfo": {"name": "my-agent", "version": "1.0"}}}
← {"jsonrpc": "2.0", "id": 1, "result": {
"protocolVersion": "2025-03-26",
"capabilities": {"tools": {"listChanged": false}},
"serverInfo": {"name": "intake", "version": "1.0.0", "mode": "live"}}}
The mode field in serverInfo reflects the credential used to connect — "test" for test API keys or test OAuth clients, "live" for production credentials.
2. List tools — Discover available tools and their input schemas.
→ {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}
← {"jsonrpc": "2.0", "id": 2, "result": {"tools": [...]}}
3. Call a tool — Execute a tool with arguments.
→ {"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {"name": "list_clients", "arguments": {}}}
← {"jsonrpc": "2.0", "id": 3, "result": {
"content": [{"type": "text", "text": "[{...}]"}]}}
Tool results are returned as JSON text in the content array. Errors set isError: true:
← {"jsonrpc": "2.0", "id": 4, "result": {
"content": [{"type": "text", "text": "Request not found"}],
"isError": true}}
Example: Full Workflow
An agent can handle a complete intake flow without any dashboard interaction:
1. create_client(name, email)
2. create_request(client_id, title, items)
3. attach_inline_form(request_id, name, fields) — or attach_form_to_request
4. upload_pdf_template(file_base64, filename) — optional
5. attach_pdf_to_request(request_id, template_id) — optional
6. send_request(request_id)
7. get_request(id) → poll for completion
8. get_form_responses(request_id) → read submitted data
tools/call → list_clients
list_clients
List all clients
tools/call → create_client
create_client
Create a new client
tools/call → update_client
update_client
Update a client's name, email, or phone
tools/call → list_requests
list_requests
List document requests
tools/call → get_request
get_request
Get request with items, uploads, and portal URL
tools/call → create_request
create_request
Create a request with checklist items
tools/call → update_request
update_request
Update title, description, due date, reminders, max_reminders, notify_professional_after_days
tools/call → add_checklist_item
add_checklist_item
Add a document item to a request
tools/call → update_item
update_item
Set review status and note on an item
tools/call → re_request_item
re_request_item
Re-request a rejected item
tools/call → send_request
send_request
Send a draft request to the client (supports skip_email)
tools/call → complete_request
complete_request
Mark a sent request as complete
tools/call → list_request_reminders
list_request_reminders
List reminder audit trail for a request
tools/call → list_uploads
list_uploads
List uploads for a request
tools/call → list_form_templates
list_form_templates
List form templates
tools/call → get_form_template
get_form_template
Get template with fields
tools/call → create_form_template
create_form_template
Create template with fields
tools/call → add_form_field
add_form_field
Add a field to a form template
tools/call → attach_form_to_request
attach_form_to_request
Attach a form template to a request
tools/call → attach_inline_form
attach_inline_form
Create and attach a one-off form
tools/call → get_form_responses
get_form_responses
Get submitted form responses
tools/call → list_pdf_templates
list_pdf_templates
List PDF templates
tools/call → get_pdf_template
get_pdf_template
Get template with field info
tools/call → upload_pdf_template
upload_pdf_template
Upload a PDF (base64-encoded)
tools/call → attach_pdf_to_request
attach_pdf_to_request
Attach a PDF to a request as a fill request
Sandbox Mode
Test your integrations without affecting production data. Same API, same endpoints, same MCP tools — just use a test key.
How It Works
- Create a test API key in Settings → API Keys (select "Test" mode)
- Use the test key in your integration — same
Authorization: Bearerheader, same endpoints - Toggle to Test mode in the dashboard to view sandbox data and captured emails
- When ready, swap to a live key — no code changes needed
What's Different in Sandbox
| Feature | Live Mode | Test Mode |
|---|---|---|
| Data | Production data | Isolated sandbox data |
| Emails | Sent to real recipients | Captured in Sandbox Inbox |
| Plan limits | Enforced | Bypassed (unlimited test data) |
| Webhooks | Delivered with "mode": "live" | Delivered with "mode": "test" |
| File uploads | Normal | Normal |
| Portal links | Normal | Shows "test request" banner |
| MCP tools | Production data | Sandbox data |
OAuth Clients (MCP)
When registering an OAuth client via dynamic registration (POST /oauth/register), include "mode": "test" to create a test-mode client. All tokens issued by that client will access sandbox data only.
POST /oauth/register
{
"client_name": "My Test Agent",
"redirect_uris": ["https://example.com/callback"],
"mode": "test"
}
Sandbox Inbox
In test mode, a Sandbox Inbox appears in the dashboard sidebar. Emails that would normally be sent to clients are captured here instead, showing the exact HTML that would have been delivered. Click any email to preview it, including working portal links.
Make.com Integration
Connect Intake to 1,000+ apps using Make.com (formerly Integromat). Here's a step-by-step guide:
1. Create a webhook in Make.com
Create a new scenario and add a "Webhooks" module → "Custom webhook". Copy the webhook URL.
2. Register the webhook in Intake
Use the API to create a webhook pointing to your Make.com URL:
POST /api/v1/webhooks
{"url": "https://hook.make.com/your-id", "events": ["request.completed"]}
3. Map the data
When Make.com receives its first webhook, it will learn the data structure. Use the fields to trigger actions in other apps — create a row in Google Sheets, send a Slack notification, update a CRM, etc.
4. Common recipes
- Request completed → Slack notification — Get notified when a client finishes uploading documents
- Upload created → Google Drive — Automatically save uploaded files to a specific folder
- Deal created → CRM update — Sync new deals to your CRM system
Testing with Sandbox
Before going live, test your Make.com scenario with sandbox data:
- Create a test API key in Settings → API Keys
- Use the test key when registering your webhook:
Authorization: Bearer dk_test_... - Trigger test events — Make.com will receive webhooks with
"mode": "test"in the payload - Validate your scenario works end-to-end
- Swap to your live API key to go live
Zapier Integration
Connect Intake to 6,000+ apps using Zapier. Use webhooks to trigger Zaps when events occur in Intake.
1. Create a Catch Hook in Zapier
Create a new Zap and choose Webhooks by Zapier → Catch Hook as the trigger. Copy the webhook URL.
2. Register the webhook in Intake
POST /api/v1/webhooks
Authorization: Bearer YOUR_API_KEY
{"url": "https://hooks.zapier.com/hooks/catch/your-id", "events": ["request.completed", "document.uploaded"]}
3. Test and map fields
Trigger a test event (send a document request to a test client), then return to Zapier to map the incoming fields to your action step.
Testing with Sandbox
- Create a test API key in Settings → API Keys
- Register your webhook using the test key
- Create and send test requests — Zapier receives webhooks with
"mode": "test" - Build and validate your Zap with sandbox data
- When ready, create a new webhook with your live key to go live
Twin.so / MCP Client Setup
Connect any MCP-compatible AI agent (Twin.so, Claude Desktop, custom agents) to Intake.
Quick Start with API Key
Configure your MCP client with the Intake server URL and an API key:
{
"url": "https://dointake.com/mcp",
"headers": {"Authorization": "Bearer YOUR_API_KEY"}
}
OAuth 2.1 (Recommended for Production)
For production integrations, use OAuth 2.1 with dynamic client registration:
- Register your client:
POST /oauth/register - Direct user to authorize:
GET /oauth/authorize - Exchange code for token:
POST /oauth/token - Use the token:
Authorization: Bearer ACCESS_TOKEN
See the MCP Protocol section for the full protocol flow.
Testing with Sandbox
- Create a test API key or register an OAuth client with
"mode": "test" - Connect your MCP client with test credentials
- The initialize response will show
"mode": "test"inserverInfo - All tool calls create and access sandbox data
- Emails are captured in the Sandbox Inbox — check the dashboard
- When satisfied, swap to live credentials — same tools, real data
Zoho One Integration
Connect Intake to Zoho One using Zoho Flow or Deluge scripts.
Option 1: Zoho Flow (No Code)
- In Zoho Flow, create a new flow with a Webhook trigger
- Copy the webhook URL
- Register it in Intake:
POST /api/v1/webhooks
Authorization: Bearer YOUR_API_KEY
{"url": "https://flow.zoho.com/your-webhook-url", "events": ["request.completed"]}
- Map incoming webhook fields to Zoho CRM, Zoho Books, or other Zoho apps
Option 2: Deluge Scripts (Custom Logic)
Call the Intake API directly from Deluge scripts in Zoho CRM, Creator, or other Zoho apps:
// Deluge script example: List clients
headers = Map();
headers.put("Authorization", "Bearer YOUR_API_KEY");
response = invokeurl
[
url: "https://dointake.com/api/v1/clients"
type: GET
headers: headers
];
Testing with Sandbox
- Create a test API key in Intake (Settings → API Keys, mode: Test)
- Use
dk_test_...key in your Zoho Flow webhooks or Deluge scripts - Test your entire workflow with sandbox data
- Swap to your
dk_live_...key when ready to go live