REST API reference
Dubsado API
14 endpoints for Dubsado. Each one is a POST with a JSON body, called with your API key.
- Base URL
- https://cotlet.dev/api/v1/dubsado
- Connects with
- Your own Dubsado account; team members see what their permission level allows
- Integration page
- Dubsado
| Endpoint | Access | Description |
|---|---|---|
| list_projects | read | Projects with client, lead or job, status, tags and project date. |
| get_project | read | One project: client, status, dates, tags, notes, forms and invoices. |
| list_clients | read | Clients with name, email, phone and their projects. |
| list_form_templates | read | Your form templates by type, to pick one to apply to a project. |
| list_forms | read | Forms across projects, or in one project, with type and status. |
| get_form_responses | read | Answers from a completed form or lead capture form, as question and answer pairs. |
| list_invoices | read | Invoices with project, total, amount paid, balance and due date. |
| create_project | write | New project for a new or existing client. Nothing is sent to the client. |
| update_project_status | write | Move a project to another of your statuses. |
| apply_form_template | write | Add a project copy of a proposal, contract, questionnaire or sub-agreement. Not sent yet. |
| send_form | write | Email a proposal, contract, questionnaire or sub-agreement to the client. |
| create_invoice | write | Draft an invoice in a project from your packages or line items. Not emailed yet. |
| send_invoice | write | Email the invoice to the client with a link to it. |
| record_manual_payment | write | Log a payment received outside Dubsado against an invoice. |
List projects
readPOST https://cotlet.dev/api/v1/dubsado/list_projects
Lists projects newest first. Category is lead or job and follows the status: Dubsado statuses are either lead or job statuses. Filter by status name, category or creation date to see what came in since your last check. Pages hold up to 100 projects; pass next_cursor back as cursor for the next page.
Parameters
| Name | Type | Description |
|---|---|---|
| category | enum | lead, job or all. Default: all. |
| status | string | Your project status name, e.g. "Proposal Sent". |
| created_since | date | Only projects created on or after this date. |
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | Next page cursor from the previous call. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/list_projects \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("dubsado", "list_projects") const result = await cotlet.call('dubsado', 'list_projects'); cotlet call dubsado list_projects Example response
{
"projects": [
{
"project_id": "prj_3d81b07e",
"title": "Spring brand shoot",
"client": {
"client_id": "cli_41a7e2",
"name": "Maya Chen",
"email": "maya@example.com"
},
"category": "lead",
"status": "Proposal Sent",
"tags": [
"Brand"
],
"project_date": "2027-04-18",
"created_at": "2026-10-07T13:12:00Z"
}
],
"next_cursor": null
} Get project
readPOST https://cotlet.dev/api/v1/dubsado/get_project
Returns one project with its client, status, tags and notes, the forms in it with their status, and its invoices with balances. Use it to read a whole project in one call before you act on it.
Parameters
| Name | Type | Description |
|---|---|---|
| project_id required | string | Project ID from list_projects. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/get_project \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"project_8c41d2"}' result = client.call("dubsado", "get_project", {"project_id":"project_8c41d2"}) const result = await cotlet.call('dubsado', 'get_project', {"project_id":"project_8c41d2"}); cotlet call dubsado get_project --data '{"project_id":"project_8c41d2"}' Example response
{
"project_id": "prj_3d81b07e",
"title": "Spring brand shoot",
"client": {
"client_id": "cli_41a7e2",
"name": "Maya Chen",
"email": "maya@example.com"
},
"category": "lead",
"status": "Proposal Sent",
"tags": [
"Brand"
],
"project_date": "2027-04-18",
"notes": "Referred by a past client.",
"forms": [
{
"form_id": "frm_6f2a91c4",
"type": "proposal",
"name": "Brand Photography proposal",
"status": "sent"
}
],
"invoices": []
} List clients
readPOST https://cotlet.dev/api/v1/dubsado/list_clients
Lists clients, the people and companies you work with. Search by name or email. Each client lists its project IDs, which you pass to get_project. Pages hold up to 100 clients.
Parameters
| Name | Type | Description |
|---|---|---|
| query | string | Search by name or email. |
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | Next page cursor from the previous call. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/list_clients \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("dubsado", "list_clients") const result = await cotlet.call('dubsado', 'list_clients'); cotlet call dubsado list_clients Example response
{
"clients": [
{
"client_id": "cli_41a7e2",
"name": "Maya Chen",
"email": "maya@example.com",
"phone": "+1 555 0100",
"projects": [
"prj_3d81b07e"
]
}
],
"next_cursor": null
} List form templates
readPOST https://cotlet.dev/api/v1/dubsado/list_form_templates
Returns the templates you built in Dubsado: proposals, contracts, questionnaires, sub-agreements and lead capture forms. Pass template_id to apply_form_template. Templates are read only here.
Parameters
| Name | Type | Description |
|---|---|---|
| type | enum | proposal, contract, questionnaire, sub_agreement, lead_capture or all. Default: all. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/list_form_templates \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("dubsado", "list_form_templates") const result = await cotlet.call('dubsado', 'list_form_templates'); cotlet call dubsado list_form_templates Example response
{
"templates": [
{
"template_id": "tpl_5c20aa",
"name": "Brand Photography proposal",
"type": "proposal"
},
{
"template_id": "tpl_81d9f3",
"name": "Photography contract",
"type": "contract"
},
{
"template_id": "tpl_2e7b60",
"name": "Shoot questionnaire",
"type": "questionnaire"
}
]
} List forms
readPOST https://cotlet.dev/api/v1/dubsado/list_forms
Lists forms sent from projects, the same forms as Forms, All forms in Dubsado. Status is sent, viewed, action_required or completed. Action required means a contract waits for your countersignature. Filter by type, status or project. Pass form_id to get_form_responses.
Parameters
| Name | Type | Description |
|---|---|---|
| project_id | string | Only forms in this project. |
| type | enum | proposal, contract, questionnaire, sub_agreement, lead_capture or all. Default: all. |
| status | string[] | Any of sent, viewed, action_required, completed. Default: all statuses. |
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | Next page cursor from the previous call. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/list_forms \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"contract","status":["sent","viewed","action_required"]}' result = client.call("dubsado", "list_forms", {"type":"contract","status":["sent","viewed","action_required"]}) const result = await cotlet.call('dubsado', 'list_forms', {"type":"contract","status":["sent","viewed","action_required"]}); cotlet call dubsado list_forms --data '{"type":"contract","status":["sent","viewed","action_required"]}' Example response
{
"forms": [
{
"form_id": "frm_a4410c7e",
"project_id": "prj_8e15c3a2",
"project_title": "Acme rebrand",
"type": "contract",
"name": "Design services contract",
"status": "viewed"
}
],
"next_cursor": null
} Get form responses
readPOST https://cotlet.dev/api/v1/dubsado/get_form_responses
Returns the answers a client gave, in the order of the form, the same as the Responses view on the form. Only completed forms have responses. Questionnaires can be edited after completion, so the answers can change.
Parameters
| Name | Type | Description |
|---|---|---|
| form_id required | string | Form ID from list_forms or apply_form_template. |
| include_empty | boolean | Include unanswered fields. Default: false. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/get_form_responses \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form_id":"frm_9b42e6d1","include_empty":true}' result = client.call("dubsado", "get_form_responses", {"form_id":"frm_9b42e6d1","include_empty":True}) const result = await cotlet.call('dubsado', 'get_form_responses', {"form_id":"frm_9b42e6d1","include_empty":true}); cotlet call dubsado get_form_responses --data '{"form_id":"frm_9b42e6d1","include_empty":true}' Example response
{
"form_id": "frm_9b42e6d1",
"type": "questionnaire",
"name": "Shoot questionnaire",
"project_id": "prj_3d81b07e",
"responses": [
{
"question": "What is the shoot for?",
"answer": "New website and press kit."
},
{
"question": "Preferred location",
"answer": "Studio, downtown"
}
]
} List invoices
readPOST https://cotlet.dev/api/v1/dubsado/list_invoices
Lists invoices, newest first. The due date comes from the invoice payment plan and is empty when the invoice has none. Balance is the total minus the payments logged, including those logged by hand. Pages hold up to 100 invoices.
Parameters
| Name | Type | Description |
|---|---|---|
| status | enum | unpaid, paid or all. Default: unpaid. |
| project_id | string | Only invoices in this project. |
| due_before | date | Only invoices with a payment due before this date. |
| limit | integer | 1 to 100. Default: 25. |
| cursor | string | Next page cursor from the previous call. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/list_invoices \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("dubsado", "list_invoices") const result = await cotlet.call('dubsado', 'list_invoices'); cotlet call dubsado list_invoices Example response
{
"invoices": [
{
"invoice_id": "inv_20417",
"project_id": "prj_8e15c3a2",
"project_title": "Acme rebrand",
"number": "0042",
"total": "1200.00",
"paid": "400.00",
"balance": "800.00",
"due_date": "2026-10-11"
}
],
"next_cursor": null
} Create project
writePOST https://cotlet.dev/api/v1/dubsado/create_project
Creates a project from a title and a client email. If the email is not in Dubsado, a new client is created, with client_name if you give one. The status must be one of your project statuses and decides whether the project is a lead or a job. This call sends nothing to the client, so it asks no confirmation. You can archive or delete the project in Dubsado.
Parameters
| Name | Type | Description |
|---|---|---|
| title required | string | Project title, e.g. "Brand Photography". |
| client_email required | string | Existing client, or a new one is created. |
| client_name | string | Name for a new client. |
| status | string | One of your project status names. |
| project_date | date | Project date. |
| tags | string[] | Project tags. |
| notes | string | Notes on the project. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/create_project \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Spring brand shoot","client_email":"maya@example.com","client_name":"Maya Chen","status":"Proposal Sent","project_date":"2027-04-18"}' result = client.call("dubsado", "create_project", {"title":"Spring brand shoot","client_email":"maya@example.com","client_name":"Maya Chen","status":"Proposal Sent","project_date":"2027-04-18"}) const result = await cotlet.call('dubsado', 'create_project', {"title":"Spring brand shoot","client_email":"maya@example.com","client_name":"Maya Chen","status":"Proposal Sent","project_date":"2027-04-18"}); cotlet call dubsado create_project --data '{"title":"Spring brand shoot","client_email":"maya@example.com","client_name":"Maya Chen","status":"Proposal Sent","project_date":"2027-04-18"}' Example response
{
"project_id": "prj_3d81b07e",
"title": "Spring brand shoot",
"client": {
"client_id": "cli_41a7e2",
"name": "Maya Chen",
"email": "maya@example.com"
},
"category": "lead",
"status": "Inquiry",
"created_at": "2026-10-11T09:00:00Z"
} Update project status
writePOST https://cotlet.dev/api/v1/dubsado/update_project_status
Moves one project to a status you already have. A project can move from a lead status to a job status; Dubsado does not turn a status from one kind into the other. The call sends nothing to the client and you undo it with one more call, so it asks no confirmation. A Flow that starts on a status change still runs.
Parameters
| Name | Type | Description |
|---|---|---|
| project_id required | string | Project ID from list_projects. |
| status required | string | One of your project status names. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/update_project_status \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"project_8c41d2","status":"status"}' result = client.call("dubsado", "update_project_status", {"project_id":"project_8c41d2","status":"status"}) const result = await cotlet.call('dubsado', 'update_project_status', {"project_id":"project_8c41d2","status":"status"}); cotlet call dubsado update_project_status --data '{"project_id":"project_8c41d2","status":"status"}' Example response
{
"project_id": "prj_3d81b07e",
"previous_status": "Proposal Sent",
"status": "Booked",
"category": "job"
} Apply form template
writePOST https://cotlet.dev/api/v1/dubsado/apply_form_template
Adds a copy of a template to one project and returns its form_id. Changes to the copy do not touch your template. Nothing is sent and the form is not added to the client portal, so this call asks no confirmation. Send it with send_form, or remove the copy in Dubsado.
Parameters
| Name | Type | Description |
|---|---|---|
| project_id required | string | Project ID from list_projects. |
| template_id required | string | Template ID from list_form_templates. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/apply_form_template \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"project_8c41d2","template_id":"template_8c41d2"}' result = client.call("dubsado", "apply_form_template", {"project_id":"project_8c41d2","template_id":"template_8c41d2"}) const result = await cotlet.call('dubsado', 'apply_form_template', {"project_id":"project_8c41d2","template_id":"template_8c41d2"}); cotlet call dubsado apply_form_template --data '{"project_id":"project_8c41d2","template_id":"template_8c41d2"}' Example response
{
"form_id": "frm_6f2a91c4",
"project_id": "prj_3d81b07e",
"type": "proposal",
"name": "Brand Photography proposal",
"status": "not_sent"
} POST https://cotlet.dev/api/v1/dubsado/send_form
Emails the form to the client with a link to it. Without subject and message it uses your default email template for the form. After the client completes it, the form is locked, except questionnaires. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| form_id required | string | Form ID from list_forms or apply_form_template. |
| subject | string | Email subject. Default: from your email template. |
| message | string | Email body. Default: from your email template. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/send_form \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"}' result = client.call("dubsado", "send_form", {"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"}) const result = await cotlet.call('dubsado', 'send_form', {"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"}); cotlet call dubsado send_form --data '{"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"}' Example response
{
"form_id": "frm_6f2a91c4",
"project_id": "prj_3d81b07e",
"type": "proposal",
"status": "sent",
"sent_to": "maya@example.com",
"sent_at": "2026-10-11T09:02:00Z"
} POST https://cotlet.dev/api/v1/dubsado/create_invoice
Creates an invoice in a project from saved packages, line items, or both. Dubsado numbers it automatically. The invoice date is the issue date; due_date adds a payment plan with one payment on that date. An invoice worth more than $0 appears in the client portal before it is emailed, so this call needs your OK. Email it with send_invoice. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| project_id required | string | Project ID from list_projects. |
| package_ids | string[] | IDs of your saved packages to add. |
| line_items | object[] | List of { name, quantity, price, description }. |
| due_date | date | Adds a payment plan with one payment due on this date. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/create_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":"project_8c41d2"}' result = client.call("dubsado", "create_invoice", {"project_id":"project_8c41d2"}) const result = await cotlet.call('dubsado', 'create_invoice', {"project_id":"project_8c41d2"}); cotlet call dubsado create_invoice --data '{"project_id":"project_8c41d2"}' Example response
{
"invoice_id": "inv_20417",
"project_id": "prj_8e15c3a2",
"number": "0042",
"status": "not_sent",
"line_items": [
{
"name": "Logo package",
"quantity": 1,
"price": "1200.00"
}
],
"total": "1200.00",
"balance": "1200.00",
"due_date": "2026-10-25"
} POST https://cotlet.dev/api/v1/dubsado/send_invoice
Emails the invoice to the client with the invoice link. Without a message it uses your default email template. After it is sent, the automatic payment reminders of Dubsado follow the payment plan. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| invoice_id required | string | Invoice ID from list_invoices or create_invoice. |
| message | string | Email body. Default: from your email template. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/send_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"invoice_id":"invoice_8c41d2"}' result = client.call("dubsado", "send_invoice", {"invoice_id":"invoice_8c41d2"}) const result = await cotlet.call('dubsado', 'send_invoice', {"invoice_id":"invoice_8c41d2"}); cotlet call dubsado send_invoice --data '{"invoice_id":"invoice_8c41d2"}' Example response
{
"invoice_id": "inv_20417",
"status": "sent",
"sent_to": "client@example.com",
"sent_at": "2026-10-11T09:05:00Z"
} POST https://cotlet.dev/api/v1/dubsado/record_manual_payment
Logs a payment you received outside Dubsado, such as cash or a check, on one invoice. The amount cannot be more than the remaining balance, and a payment for the full balance moves the invoice to Paid. It does not cancel autopay. Only manually logged payments can be edited or deleted, in Dubsado. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| invoice_id required | string | Invoice ID from list_invoices or create_invoice. |
| amount required | string | Amount, e.g. "400.00". Not more than the remaining balance. |
| paid_on required | date | Date received. |
| method required | enum | cash, check, credit, e_transfer, paypal, stripe, square, authorize_net or other. |
| description | string | Optional note on the payment. |
Request
curl -X POST https://cotlet.dev/api/v1/dubsado/record_manual_payment \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"invoice_id":"inv_20417","amount":"400.00","paid_on":"2026-10-09","method":"e_transfer","description":"E-transfer ref 7731"}' result = client.call("dubsado", "record_manual_payment", {"invoice_id":"inv_20417","amount":"400.00","paid_on":"2026-10-09","method":"e_transfer","description":"E-transfer ref 7731"}) const result = await cotlet.call('dubsado', 'record_manual_payment', {"invoice_id":"inv_20417","amount":"400.00","paid_on":"2026-10-09","method":"e_transfer","description":"E-transfer ref 7731"}); cotlet call dubsado record_manual_payment --data '{"invoice_id":"inv_20417","amount":"400.00","paid_on":"2026-10-09","method":"e_transfer","description":"E-transfer ref 7731"}' Example response
{
"invoice_id": "inv_20417",
"amount": "400.00",
"method": "check",
"paid_on": "2026-10-12",
"balance": "800.00",
"invoice_status": "unpaid"
}