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
Dubsado Endpoints
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.

POST 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

Parameters: list_projects
NameTypeDescription
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
curl -X POST https://cotlet.dev/api/v1/dubsado/list_projects \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("dubsado", "list_projects")
Node.js
const result = await cotlet.call('dubsado', 'list_projects');
CLI
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
}

POST 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

Parameters: get_project
NameTypeDescription
project_id required string Project ID from list_projects.

Request

cURL
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"}'
Python
result = client.call("dubsado", "get_project", {"project_id":"project_8c41d2"})
Node.js
const result = await cotlet.call('dubsado', 'get_project', {"project_id":"project_8c41d2"});
CLI
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": []
}

POST 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

Parameters: list_clients
NameTypeDescription
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
curl -X POST https://cotlet.dev/api/v1/dubsado/list_clients \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("dubsado", "list_clients")
Node.js
const result = await cotlet.call('dubsado', 'list_clients');
CLI
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
}

POST 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

Parameters: list_form_templates
NameTypeDescription
type enum proposal, contract, questionnaire, sub_agreement, lead_capture or all. Default: all.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/dubsado/list_form_templates \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("dubsado", "list_form_templates")
Node.js
const result = await cotlet.call('dubsado', 'list_form_templates');
CLI
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

read

POST 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

Parameters: list_forms
NameTypeDescription
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
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"]}'
Python
result = client.call("dubsado", "list_forms", {"type":"contract","status":["sent","viewed","action_required"]})
Node.js
const result = await cotlet.call('dubsado', 'list_forms', {"type":"contract","status":["sent","viewed","action_required"]});
CLI
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
}

POST 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

Parameters: get_form_responses
NameTypeDescription
form_id required string Form ID from list_forms or apply_form_template.
include_empty boolean Include unanswered fields. Default: false.

Request

cURL
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}'
Python
result = client.call("dubsado", "get_form_responses", {"form_id":"frm_9b42e6d1","include_empty":True})
Node.js
const result = await cotlet.call('dubsado', 'get_form_responses', {"form_id":"frm_9b42e6d1","include_empty":true});
CLI
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"
    }
  ]
}

POST 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

Parameters: list_invoices
NameTypeDescription
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
curl -X POST https://cotlet.dev/api/v1/dubsado/list_invoices \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("dubsado", "list_invoices")
Node.js
const result = await cotlet.call('dubsado', 'list_invoices');
CLI
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
}

POST 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

Parameters: create_project
NameTypeDescription
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
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"}'
Python
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"})
Node.js
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"});
CLI
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"
}

POST 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

Parameters: update_project_status
NameTypeDescription
project_id required string Project ID from list_projects.
status required string One of your project status names.

Request

cURL
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"}'
Python
result = client.call("dubsado", "update_project_status", {"project_id":"project_8c41d2","status":"status"})
Node.js
const result = await cotlet.call('dubsado', 'update_project_status', {"project_id":"project_8c41d2","status":"status"});
CLI
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"
}

POST 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

Parameters: apply_form_template
NameTypeDescription
project_id required string Project ID from list_projects.
template_id required string Template ID from list_form_templates.

Request

cURL
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"}'
Python
result = client.call("dubsado", "apply_form_template", {"project_id":"project_8c41d2","template_id":"template_8c41d2"})
Node.js
const result = await cotlet.call('dubsado', 'apply_form_template', {"project_id":"project_8c41d2","template_id":"template_8c41d2"});
CLI
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"
}

Send form

write Confirm

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

Parameters: send_form
NameTypeDescription
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
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"}'
Python
result = client.call("dubsado", "send_form", {"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"})
Node.js
const result = await cotlet.call('dubsado', 'send_form', {"form_id":"frm_6f2a91c4","subject":"Your proposal for Spring brand shoot"});
CLI
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"
}

Create invoice

write Confirm

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

Parameters: create_invoice
NameTypeDescription
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
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"}'
Python
result = client.call("dubsado", "create_invoice", {"project_id":"project_8c41d2"})
Node.js
const result = await cotlet.call('dubsado', 'create_invoice', {"project_id":"project_8c41d2"});
CLI
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"
}

Send invoice

write Confirm

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

Parameters: send_invoice
NameTypeDescription
invoice_id required string Invoice ID from list_invoices or create_invoice.
message string Email body. Default: from your email template.

Request

cURL
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"}'
Python
result = client.call("dubsado", "send_invoice", {"invoice_id":"invoice_8c41d2"})
Node.js
const result = await cotlet.call('dubsado', 'send_invoice', {"invoice_id":"invoice_8c41d2"});
CLI
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"
}

Record manual payment

write Confirm

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

Parameters: record_manual_payment
NameTypeDescription
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
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"}'
Python
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"})
Node.js
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"});
CLI
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"
}