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:

FreePro ($19/month)
Clients10Unlimited
Deals3Unlimited
Parties per deal5Unlimited

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:

ScopeAccess
readList and retrieve resources
writeCreate and update resources
webhooksManage 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 PrefixModeData Access
dk_live_...LiveProduction data only
dk_test_...TestSandbox 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.

GET /api/v1/me

Test your API key

Returns information about the authenticated user.

Scope: read
Request
curl https://dointake.com/api/v1/me \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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.

StatusMeaning
200Success
201Created
204Deleted (no content)
401Unauthorized — missing or invalid API key
403Forbidden — insufficient scope or plan limit exceeded
404Not found
422Validation error
429Rate limited
500Internal server error
GET /api/v1/...

Error response format

All errors follow this structure. Validation errors include a `details` object mapping field names to error messages.

Response
{
  "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=term to filter by name or email; ?external_id=x&external_source=y to match by external system; ?tags=vip,residential to filter by tags
  • Requests: ?status=pending&client_id=xxx&deal_id=xxx
  • Deals: ?status=active; ?external_id=x&external_source=y to match by external system

Clients

Manage your clients. Clients are the people you collect documents from.

GET /api/v1/clients

List clients

Returns all clients for your account. Optionally filter by search term, external ID, or tags.

Scope: read
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)
Request
curl https://dointake.com/api/v1/clients?search=jane \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
    }
  ]
}
GET /api/v1/clients/:id

Get a client

Retrieve a single client by their ID.

Scope: read
Parameter Type Required Description
id string required Client ID
Request
curl https://dointake.com/api/v1/clients/cl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
  }
}
POST /api/v1/clients

Create a client

Create a new client.

Scope: write
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
Request
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"}'
Response
{
  "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
  }
}
PATCH /api/v1/clients/:id

Update a client

Update a client's name, email, phone, or metadata.

Scope: write
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
Request
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"}'
Response
{
  "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
  }
}
PUT /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.

Scope: write
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
Request
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"}'
Response
{
  "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.

GET /api/v1/requests

List requests

Returns all document requests. Filter by status, client, or deal.

Scope: read
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
Request
curl "https://dointake.com/api/v1/requests?status=sent" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
GET /api/v1/requests/:id

Get a request

Retrieve a single request with its checklist items.

Scope: read
Parameter Type Required Description
id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
      }
    ]
  }
}
POST /api/v1/requests

Create a request

Create a new document request for a client. Optionally include checklist items.

Scope: write
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
Request
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"}]}'
Response
{
  "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
  }
}
PATCH /api/v1/requests/:id

Update a request

Update an existing document request.

Scope: write
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)
Request
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"}'
Response
{
  "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
  }
}
POST /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).

Scope: write
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.
Request
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/send \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
  }
}
POST /api/v1/requests/:id/complete

Complete a request

Mark a document request as complete.

Scope: write
Parameter Type Required Description
id string required Request ID
Request
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/complete \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
  }
}
POST /api/v1/requests/:id/reopen

Reopen a request

Reopen a completed document request.

Scope: write
Parameter Type Required Description
id string required Request ID
Request
curl -X POST https://dointake.com/api/v1/requests/req_xyz789/reopen \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
  }
}
GET /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.

Scope: read
Parameter Type Required Description
request_id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789/reminders \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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.

GET /api/v1/requests/:request_id/items

List checklist items

Returns all checklist items for a given request.

Scope: read
Parameter Type Required Description
request_id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789/items \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
POST /api/v1/requests/:request_id/items

Create a checklist item

Add a new checklist item to a request.

Scope: write
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)
Request
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"}'
Response
{
  "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
  }
}
PATCH /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).

Scope: write
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)
Request
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"}'
Response
{
  "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
  }
}
POST /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.

Scope: write
Parameter Type Required Description
id string required Item ID
Request
curl -X POST https://dointake.com/api/v1/items/itm_abc123/re-request \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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.

GET /api/v1/requests/:request_id/uploads

List uploads

Returns all uploads for a given request.

Scope: read
Parameter Type Required Description
request_id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789/uploads \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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.

GET /api/v1/form-templates

List form templates

Returns all form templates for your account.

Scope: read
Request
curl https://dointake.com/api/v1/form-templates \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
GET /api/v1/form-templates/:id

Get a form template

Retrieve a form template with all its field definitions.

Scope: read
Parameter Type Required Description
id string required Template ID
Request
curl https://dointake.com/api/v1/form-templates/tmpl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
  }
}
POST /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.

Scope: write
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
Request
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"}}]}'
Response
{
  "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"
  }
}
DELETE /api/v1/form-templates/:id

Delete a form template

Delete a form template. Returns 409 if the template is attached to any requests.

Scope: write
Parameter Type Required Description
id string required Template ID
Request
curl -X DELETE https://dointake.com/api/v1/form-templates/tmpl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
204 No Content
POST /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.

Scope: write
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
Request
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"]}}'
Response
{
  "data": {
    "id": 4,
    "label": "State",
    "position": 3,
    "type": "select",
    "config": {
      "options": [
        "CA",
        "NY",
        "TX"
      ]
    },
    "description": null,
    "required": false
  }
}
PATCH /api/v1/form-templates/:form_template_id/fields/:id

Update a field

Update a field on a form template.

Scope: write
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
Request
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}'
Response
{
  "data": {
    "id": 4,
    "label": "Updated Label",
    "position": 3,
    "type": "select",
    "config": {
      "options": [
        "CA",
        "NY",
        "TX"
      ]
    },
    "description": null,
    "required": true
  }
}
DELETE /api/v1/form-templates/:form_template_id/fields/:id

Delete a field

Remove a field from a form template.

Scope: write
Parameter Type Required Description
form_template_id string required Template ID
id integer required Field ID
Request
curl -X DELETE https://dointake.com/api/v1/form-templates/tmpl_abc123/fields/4 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
204 No Content

Request Forms

Attach form templates or inline forms to requests. Clients fill them out in the portal alongside document uploads.

POST /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.

Scope: write
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)
Request
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"}'
Response
{
  "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"
  }
}
GET /api/v1/requests/:request_id/forms

List attached forms

List all forms attached to a request. Completed forms include responses.

Scope: read
Parameter Type Required Description
request_id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789/forms \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
        }
      ]
    }
  ]
}
DELETE /api/v1/requests/:request_id/forms/:id

Detach a form

Remove an attached form from a request. Also deletes any responses.

Scope: write
Parameter Type Required Description
request_id string required Request ID
id string required Request form ID (sub_xxx)
Request
curl -X DELETE https://dointake.com/api/v1/requests/req_xyz789/forms/sub_def456 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
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.

GET /api/v1/pdf-templates

List PDF templates

List all PDF templates for the authenticated user.

Scope: read
Request
curl https://dointake.com/api/v1/pdf-templates \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
GET /api/v1/pdf-templates/:id

Get a PDF template

Get a PDF template with field definitions and an editor URL for the placement UI.

Scope: read
Parameter Type Required Description
id string required Template ID (ptl_xxx)
Request
curl https://dointake.com/api/v1/pdf-templates/ptl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
  }
}
POST /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.

Scope: write
Parameter Type Required Description
file file required PDF file (multipart upload)
Request
curl -X POST https://dointake.com/api/v1/pdf-templates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@w9.pdf"
Response
{
  "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"
  }
}
DELETE /api/v1/pdf-templates/:id

Delete a PDF template

Delete a PDF template. Returns 409 if the template is attached to any requests.

Scope: write
Parameter Type Required Description
id string required Template ID (ptl_xxx)
Request
curl -X DELETE https://dointake.com/api/v1/pdf-templates/ptl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
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.

POST /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.

Scope: write
Parameter Type Required Description
request_id string required Request ID
pdf_template_id string required PDF template ID (ptl_xxx)
Request
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"}'
Response
{
  "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
  }
}
GET /api/v1/requests/:request_id/pdf-fills

List PDF fill requests

List all PDF fill requests for a given request.

Scope: read
Parameter Type Required Description
request_id string required Request ID
Request
curl https://dointake.com/api/v1/requests/req_xyz789/pdf-fills \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
DELETE /api/v1/requests/:request_id/pdf-fills/:id

Delete a PDF fill request

Remove a PDF fill request from a request.

Scope: write
Parameter Type Required Description
request_id string required Request ID
id string required Fill request ID (pfr_xxx)
Request
curl -X DELETE https://dointake.com/api/v1/requests/req_xyz789/pdf-fills/pfr_ghi789 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
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.

GET /api/v1/deals

List deals

Returns all deals. Optionally filter by status, external ID, or external source.

Scope: read
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)
Request
curl https://dointake.com/api/v1/deals \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
GET /api/v1/deals/:id

Get a deal

Retrieve a deal with all parties and their requests.

Scope: read
Parameter Type Required Description
id string required Deal ID
Request
curl https://dointake.com/api/v1/deals/deal_001 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
  }
}
POST /api/v1/deals

Create a deal

Create a new deal. Optionally create from a deal template by passing `template_id`.

Scope: write
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
Request
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"}'
Response
{
  "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
  }
}
PATCH /api/v1/deals/:id

Update a deal

Update an existing deal.

Scope: write
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
Request
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"}'
Response
{
  "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
  }
}
PUT /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.

Scope: write
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
Request
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"}'
Response
{
  "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
  }
}
POST /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`.

Scope: write
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}}
Request
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"}}]}'
Response
{
  "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.

GET /api/v1/deal-templates

List deal templates

Returns all deal templates available to the user, including system templates.

Scope: read
Request
curl https://dointake.com/api/v1/deal-templates \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
GET /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.

Scope: read
Parameter Type Required Description
id string required Deal template ID
Request
curl https://dointake.com/api/v1/deal-templates/dtpl_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
  }
}
POST /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.

Scope: write
Parameter Type Required Description
name string required Deal name
template_id string required Deal template ID (dtpl_xxx)
description string optional Deal description
Request
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"}'
Response
{
  "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
  }
}
POST /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.

Scope: write
Parameter Type Required Description
deal_id string required Deal ID
party_id string required Party ID
Request
curl -X POST https://dointake.com/api/v1/deals/deal_xyz789/parties/pty_001/requests/from-template \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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.

GET /api/v1/deals/:deal_id/parties

List parties

Returns all parties for a deal.

Scope: read
Parameter Type Required Description
deal_id string required Deal ID
Request
curl https://dointake.com/api/v1/deals/deal_001/parties \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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
    }
  ]
}
POST /api/v1/deals/:deal_id/parties

Create a party

Add a party to a deal. Optionally link to an existing client.

Scope: write
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)
Request
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"]}'
Response
{
  "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
  }
}
PATCH /api/v1/parties/:id

Update a party

Update a party's role or linked client.

Scope: write
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)
Request
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"}'
Response
{
  "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.

GET /api/v1/activity

List activity

Returns activity log entries for your account, newest first. Supports cursor-based pagination and filtering.

Scope: read
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)
Request
curl https://dointake.com/api/v1/activity?deal_id=deal_abc123 \
  -H "Authorization: Bearer sk_live_..."
Response
{
  "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.

GET /api/v1/webhooks

List webhooks

Returns all webhook subscriptions.

Scope: webhooks
Request
curl https://dointake.com/api/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "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"
    }
  ]
}
POST /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.

Scope: webhooks
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)
Request
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"]}'
Response
{
  "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"
  }
}
PATCH /api/v1/webhooks/:id

Update a webhook

Update a webhook subscription.

Scope: webhooks
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
Request
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}'
Response
{
  "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"
  }
}
DELETE /api/v1/webhooks/:id

Delete a webhook

Permanently delete a webhook subscription.

Scope: webhooks
Parameter Type Required Description
id string required Webhook ID
Request
curl -X DELETE https://dointake.com/api/v1/webhooks/wh_001 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
# 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:

EventFired when
client.createdA new client is created
client.updatedA client is updated
request.createdA new document request is created
request.completedA request is marked complete
request.reopenedA completed request is reopened
request.overdueA request is overdue
upload.createdA client uploads a file
pdf_form.completedA client completes a fillable PDF form
deal.createdA new deal is created
deal.updatedA deal is updated
deal.status_changedA 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.

POST Verification — Node.js

Verify in Node.js

Request
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)
  );
}
POST Verification — Python

Verify in Python

Request
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)
POST Verification — Ruby

Verify in Ruby

Request
require 'openssl'

def verify_webhook(payload, signature, secret)
  expected = OpenSSL::HMAC.hexdigest(
    'sha256', secret, payload
  )
  Rack::Utils.secure_compare(signature, expected)
end
POST Verification — PHP

Verify in PHP

Request
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:

  1. Open Settings → Connectors
  2. Click Add Connector
  3. Enter the URL: https://dointake.com/mcp
  4. 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
TOOL tools/call → list_clients

list_clients

List all clients

Scope: read
TOOL tools/call → create_client

create_client

Create a new client

Scope: write
TOOL tools/call → update_client

update_client

Update a client's name, email, or phone

Scope: write
TOOL tools/call → list_requests

list_requests

List document requests

Scope: read
TOOL tools/call → get_request

get_request

Get request with items, uploads, and portal URL

Scope: read
TOOL tools/call → create_request

create_request

Create a request with checklist items

Scope: write
TOOL tools/call → update_request

update_request

Update title, description, due date, reminders, max_reminders, notify_professional_after_days

Scope: write
TOOL tools/call → add_checklist_item

add_checklist_item

Add a document item to a request

Scope: write
TOOL tools/call → update_item

update_item

Set review status and note on an item

Scope: write
TOOL tools/call → re_request_item

re_request_item

Re-request a rejected item

Scope: write
TOOL tools/call → send_request

send_request

Send a draft request to the client (supports skip_email)

Scope: write
TOOL tools/call → complete_request

complete_request

Mark a sent request as complete

Scope: write
TOOL tools/call → list_request_reminders

list_request_reminders

List reminder audit trail for a request

Scope: read
TOOL tools/call → list_uploads

list_uploads

List uploads for a request

Scope: read
TOOL tools/call → list_form_templates

list_form_templates

List form templates

Scope: read
TOOL tools/call → get_form_template

get_form_template

Get template with fields

Scope: read
TOOL tools/call → create_form_template

create_form_template

Create template with fields

Scope: write
TOOL tools/call → add_form_field

add_form_field

Add a field to a form template

Scope: write
TOOL tools/call → attach_form_to_request

attach_form_to_request

Attach a form template to a request

Scope: write
TOOL tools/call → attach_inline_form

attach_inline_form

Create and attach a one-off form

Scope: write
TOOL tools/call → get_form_responses

get_form_responses

Get submitted form responses

Scope: read
TOOL tools/call → list_pdf_templates

list_pdf_templates

List PDF templates

Scope: read
TOOL tools/call → get_pdf_template

get_pdf_template

Get template with field info

Scope: read
TOOL tools/call → upload_pdf_template

upload_pdf_template

Upload a PDF (base64-encoded)

Scope: write
TOOL tools/call → attach_pdf_to_request

attach_pdf_to_request

Attach a PDF to a request as a fill request

Scope: write

Sandbox Mode

Test your integrations without affecting production data. Same API, same endpoints, same MCP tools — just use a test key.

How It Works

  1. Create a test API key in Settings → API Keys (select "Test" mode)
  2. Use the test key in your integration — same Authorization: Bearer header, same endpoints
  3. Toggle to Test mode in the dashboard to view sandbox data and captured emails
  4. When ready, swap to a live key — no code changes needed

What's Different in Sandbox

FeatureLive ModeTest Mode
DataProduction dataIsolated sandbox data
EmailsSent to real recipientsCaptured in Sandbox Inbox
Plan limitsEnforcedBypassed (unlimited test data)
WebhooksDelivered with "mode": "live"Delivered with "mode": "test"
File uploadsNormalNormal
Portal linksNormalShows "test request" banner
MCP toolsProduction dataSandbox 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:

  1. Create a test API key in Settings → API Keys
  2. Use the test key when registering your webhook: Authorization: Bearer dk_test_...
  3. Trigger test events — Make.com will receive webhooks with "mode": "test" in the payload
  4. Validate your scenario works end-to-end
  5. 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
  1. Create a test API key in Settings → API Keys
  2. Register your webhook using the test key
  3. Create and send test requests — Zapier receives webhooks with "mode": "test"
  4. Build and validate your Zap with sandbox data
  5. 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:

  1. Register your client: POST /oauth/register
  2. Direct user to authorize: GET /oauth/authorize
  3. Exchange code for token: POST /oauth/token
  4. Use the token: Authorization: Bearer ACCESS_TOKEN

See the MCP Protocol section for the full protocol flow.

Testing with Sandbox
  1. Create a test API key or register an OAuth client with "mode": "test"
  2. Connect your MCP client with test credentials
  3. The initialize response will show "mode": "test" in serverInfo
  4. All tool calls create and access sandbox data
  5. Emails are captured in the Sandbox Inbox — check the dashboard
  6. 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)
  1. In Zoho Flow, create a new flow with a Webhook trigger
  2. Copy the webhook URL
  3. Register it in Intake:
POST /api/v1/webhooks
Authorization: Bearer YOUR_API_KEY
{"url": "https://flow.zoho.com/your-webhook-url", "events": ["request.completed"]}
  1. 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
  1. Create a test API key in Intake (Settings → API Keys, mode: Test)
  2. Use dk_test_... key in your Zoho Flow webhooks or Deluge scripts
  3. Test your entire workflow with sandbox data
  4. Swap to your dk_live_... key when ready to go live