Dashly Docs

Deals

`/deals`

List deals

GET
/deals

One page of records, newest first unless you say otherwise, with the total count in meta so you know how far to paginate.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Query Parameters

page?integer

1-based. meta.totalPages says how many there are.

Range1 <= value
Default1
per_page?integer

Rows per page. A value above 200 returns 400 - never a silent clamp.

Range1 <= value <= 200
Default50
sort?string

Prefix with - for descending. An undeclared sort returns 400.

Default"-updatedAt"
Value in"createdAt""-createdAt""updatedAt""-updatedAt""valueCents""-valueCents""name""-name"
q?string

Case-insensitive "contains", across: name.

filter[status]?string

Exact match on status.

Value in"open""won""lost"
filter[clientId]?string

Exact match on clientId.

Response Body

The requested page.

application/json
  1. response
data*array<>
meta*
Example Request
curl -X GET 'https://api.usedashly.com/v1/deals'
Example Response
{  "data": [    {      "id": "string",      "clientId": "string",      "name": "string",      "valueCents": 0,      "status": "open",      "notes": "string",      "expectedCloseAt": "2019-08-24T14:15:22Z",      "wonAt": "2019-08-24T14:15:22Z",      "lostAt": "2019-08-24T14:15:22Z",      "lostReasonId": "string",      "lostReasonLabel": "string",      "projectId": "string",      "plannedProjectId": "string",      "revenueKind": "one_shot",      "recurrencePeriod": "monthly",      "recurrenceStartKind": "string",      "recurrenceStartsOn": "2019-08-24",      "recurrenceStartMilestoneId": "string",      "recurrenceEndsOn": "2019-08-24",      "commitmentMonths": 0,      "createdAt": "2019-08-24T14:15:22Z",      "updatedAt": "2019-08-24T14:15:22Z",      "amountCents": 0,      "partAmountCents": 0,      "customFields": {}    }  ],  "meta": {    "page": 0,    "perPage": 0,    "total": 0,    "totalPages": 0  }}

Create a deal

POST
/deals

Links are given as the target's public id (clientId: "hsbftmp3f5"); the API resolves them server-side. Fields the database requires but the interface decides for you are filled in the same way here.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
name*string
Length1 <= length <= 200
valueCents?|null
notes?string|null
expectedCloseAt?string|null
clientId?string|null
contactId?string|null
stageId?string|null
customFields?
revenueKind?string
Value in"one_shot""recurring"
recurrencePeriod?|null
recurrenceStartKind?|null
recurrenceStartsOn?|null
recurrenceStartMilestoneId?|null
recurrenceEndsOn?|null
commitmentMonths?|null
createdAt?string

When this actually happened, for an import. Past dates only; defaults to now. updatedAt follows it unless the body sets one.

Formatdate-time

Response Body

Created. The body is the new record, as a read would return it.

application/json
  1. response
data*
Example Request
curl -X POST 'https://api.usedashly.com/v1/deals' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "name": "string",    "valueCents": 0,    "status": "open",    "notes": "string",    "expectedCloseAt": "2019-08-24T14:15:22Z",    "wonAt": "2019-08-24T14:15:22Z",    "lostAt": "2019-08-24T14:15:22Z",    "lostReasonId": "string",    "lostReasonLabel": "string",    "projectId": "string",    "plannedProjectId": "string",    "revenueKind": "one_shot",    "recurrencePeriod": "monthly",    "recurrenceStartKind": "string",    "recurrenceStartsOn": "2019-08-24",    "recurrenceStartMilestoneId": "string",    "recurrenceEndsOn": "2019-08-24",    "commitmentMonths": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "amountCents": 0,    "partAmountCents": 0,    "customFields": {}  }}

Retrieve a deal

GET
/deals/{id}

The full record, custom fields included when the resource carries them. A record from another agency is a 404, not a 403 - its existence is not ours to confirm.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.

Response Body

Found.

application/json
  1. response
data*
Example Request
curl -X GET 'https://api.usedashly.com/v1/deals/string'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "name": "string",    "valueCents": 0,    "status": "open",    "notes": "string",    "expectedCloseAt": "2019-08-24T14:15:22Z",    "wonAt": "2019-08-24T14:15:22Z",    "lostAt": "2019-08-24T14:15:22Z",    "lostReasonId": "string",    "lostReasonLabel": "string",    "projectId": "string",    "plannedProjectId": "string",    "revenueKind": "one_shot",    "recurrencePeriod": "monthly",    "recurrenceStartKind": "string",    "recurrenceStartsOn": "2019-08-24",    "recurrenceStartMilestoneId": "string",    "recurrenceEndsOn": "2019-08-24",    "commitmentMonths": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "amountCents": 0,    "partAmountCents": 0,    "customFields": {}  }}

Update a deal

PATCH
/deals/{id}

A partial update: only the fields you send change. One exception - customFields is replaced wholesale, not merged, so sending it with a single key clears the others. Read the record, merge, then write.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.

Request Body

application/json
  1. body
name?string
Length1 <= length <= 200
valueCents?|null
notes?string|null
expectedCloseAt?string|null
clientId?string|null
contactId?string|null
stageId?string|null
customFields?
revenueKind?string
Value in"one_shot""recurring"
recurrencePeriod?|null
recurrenceStartKind?|null
recurrenceStartsOn?|null
recurrenceStartMilestoneId?|null
recurrenceEndsOn?|null
commitmentMonths?|null

Response Body

Updated. The body is the record as it now stands.

application/json
  1. response
data*
Example Request
curl -X PATCH 'https://api.usedashly.com/v1/deals/string' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "name": "string",    "valueCents": 0,    "status": "open",    "notes": "string",    "expectedCloseAt": "2019-08-24T14:15:22Z",    "wonAt": "2019-08-24T14:15:22Z",    "lostAt": "2019-08-24T14:15:22Z",    "lostReasonId": "string",    "lostReasonLabel": "string",    "projectId": "string",    "plannedProjectId": "string",    "revenueKind": "one_shot",    "recurrencePeriod": "monthly",    "recurrenceStartKind": "string",    "recurrenceStartsOn": "2019-08-24",    "recurrenceStartMilestoneId": "string",    "recurrenceEndsOn": "2019-08-24",    "commitmentMonths": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "amountCents": 0,    "partAmountCents": 0,    "customFields": {}  }}

Delete a deal

DELETE
/deals/{id}

Permanent. The audit log keeps the trace; the record does not come back.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.

Response Body

Deleted. Empty body.

Example Request
curl -X DELETE 'https://api.usedashly.com/v1/deals/string'
Example Response
Empty

List available actions

GET
/deals/actions

Every verb this resource answers to, with its target and the JSON Schema of its body - the same contracts this document describes, readable at runtime.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Response Body

The available verbs.

application/json
  1. response
data*array<>
Example Request
curl -X GET 'https://api.usedashly.com/v1/deals/actions'
Example Response
{  "data": [    {      "name": "string",      "label": "string",      "description": "string",      "target": "item",      "readOnly": true,      "path": "string",      "input": {}    }  ]}

Close or reopen the deal

POST
/deals/{id}/actions/close

Wins, loses or reopens the deal without moving it between stages. Winning signs the organisation and opens the delivery project (or attaches one of the client's existing projects via attachProject, adding the deal to its roadmap as a block named, dated and priced by block), and may open that project to the deal's delivery partners via sharePartners; losing requires a reason from the catalogue, given by its label.

Does what the dashboard's closeDealAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
outcome*string
Value in"won""lost""reopen"
reason?string
Lengthlength <= 120
note?string
Lengthlength <= 1000
attachProject?string
Lengthlength <= 40
sharePartners?array<>
Itemsitems <= 20
block?

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/close' \  -H 'Content-Type: application/json' \  -d '{  "outcome": "won"}'
Example Response
{  "data": null}

Convert back to a lead

POST
/deals/{id}/actions/convert-to-lead

Recreates a qualified lead from the deal - organisation, contact, notes - then archives the deal, so the pipeline keeps the history of what was attempted.

Does what the dashboard's convertDealToLeadAction does - same implementation, so the two cannot diverge. Takes no body.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/convert-to-lead'
Example Response
{  "data": null}

Duplicate the deal

POST
/deals/{id}/actions/duplicate

Copies the deal into a new opportunity on the same organisation, carrying value, contact, buying committee and product lines. The copy starts again at the pipeline's first stage.

Does what the dashboard's duplicateDealAction does - same implementation, so the two cannot diverge. Takes no body.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/duplicate'
Example Response
{  "data": null}

Merge into another deal

POST
/deals/{id}/actions/merge

Merges this deal into the target deal: activities, notes, product lines and buying committee move across, then this deal is deleted.

Does what the dashboard's mergeDealsAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
targetDealId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/merge' \  -H 'Content-Type: application/json' \  -d '{  "targetDealId": "string"}'
Example Response
{  "data": null}

Archive or restore

POST
/deals/{id}/actions/set-archived

Archives the deal - it leaves the board without losing its history - or restores it when archived is false.

Does what the dashboard's setDealArchivedAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
archived*boolean

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-archived' \  -H 'Content-Type: application/json' \  -d '{  "archived": true}'
Example Response
{  "data": null}

Change the owner

POST
/deals/{id}/actions/set-owner

Reassigns sales ownership of the deal to an agency member (see /v1/members), or clears it with ownerId: null.

Does what the dashboard's setDealOwnerAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
ownerId?|null

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-owner' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/upsert-contact

Attaches an existing contact to the deal's buying committee with their weight (decision-maker, influencer, participant), or updates that weight if they are already on it.

Does what the dashboard's upsertDealContactAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
contactId*string
Length1 <= length <= 60
role*string
Value in"decision_maker""influencer""participant"

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/upsert-contact' \  -H 'Content-Type: application/json' \  -d '{  "contactId": "string",  "role": "decision_maker"}'
Example Response
{  "data": null}

Remove a contact

POST
/deals/{id}/actions/remove-contact

Removes a contact from the deal's buying committee. The contact record itself is kept.

Does what the dashboard's removeDealContactAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
contactId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/remove-contact' \  -H 'Content-Type: application/json' \  -d '{  "contactId": "string"}'
Example Response
{  "data": null}

Add a product line

POST
/deals/{id}/actions/add-product

Appends a line to what the deal sells: name, quantity and unit price in cents.

Does what the dashboard's addDealProductAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
name*string
Length1 <= length <= 200
quantity?number
Range0 < value <= 1000000
Default1
unitAmountCents?integer
Range0 <= value <= 9007199254740991
Default0

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/add-product' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
Example Response
{  "data": null}

Log a new opportunity

POST
/deals/actions/new-opportunity

One sentence about an organisation becomes the right record: an unknown organisation is created with a lead to contact; a client gets a deal in the pipeline for existing clients; anyone else a deal in the default pipeline. A follow-up task is always planned for the owner (default: in 3 days). 409 when the organisation already has an open deal, unless onExistingDeal is create. Never creates a project.

Does what the dashboard's createOpportunityAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
clientId?string
Length1 <= length <= 60
organisationName?string
Length2 <= length <= 120
summary*string
Length1 <= length <= 200
amountCents?|null
followUpOn?string
Match^\d{4}-\d{2}-\d{2}$
ownerId?string
Length1 <= length <= 120
onExistingDeal?string
Default"fail"
Value in"fail""create"

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/actions/new-opportunity' \  -H 'Content-Type: application/json' \  -d '{  "summary": "string"}'
Example Response
{  "data": null}

Remove a product line

POST
/deals/{id}/actions/remove-product

Removes a line from what the deal sells, addressed by its public id. List the lines with /v1/deal-products?filter[dealId]=….

Does what the dashboard's removeDealProductAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
lineId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/remove-product' \  -H 'Content-Type: application/json' \  -d '{  "lineId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/link-pennylane-quote

Links a quote of the agency's Pennylane account (quoteId, Pennylane's id) to this deal. The quote is read from Pennylane with the agency's key: one that key does not reach answers 404; no Pennylane connection answers 422 not_connected. A deal can hold several quotes and linking the same pair again only refreshes its stored number, label, amount and status. Returns linked (false when it already was) and the quote as Pennylane gave it.

Does what the dashboard's linkDealPennylaneQuoteAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
quoteId*integer
Range0 < value <= 9007199254740991

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/link-pennylane-quote' \  -H 'Content-Type: application/json' \  -d '{  "quoteId": 1}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/unlink-pennylane-quote

Removes the link between this deal and a Pennylane quote (quoteId). Nothing changes in Pennylane, and it needs no Pennylane connection. Unlinking a pair that was never linked does nothing (unlinked: false).

Does what the dashboard's unlinkDealPennylaneQuoteAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
quoteId*integer
Range0 < value <= 9007199254740991

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/unlink-pennylane-quote' \  -H 'Content-Type: application/json' \  -d '{  "quoteId": 1}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/link-object

Creates a relation between this record and a target object (targetType plus the target's public targetId) in the relation graph. Linking the same pair twice does nothing.

Does what the dashboard's linkObjectAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
targetType*string
Value in"account""contact""project""ticket""deal""user""proposal"
targetId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/link-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/unlink-object

Removes the relation between this record and the target object in the relation graph. Unlinking a pair that was never linked does nothing. A deal/project pair is unlinked like unlink-project on the deal: the change is audited, so the key must be attached to a user (403 with reason key_not_attached otherwise).

Does what the dashboard's unlinkObjectAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
targetType*string
Value in"account""contact""project""ticket""deal""user""proposal"
targetId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/unlink-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/link-project

Links this deal to its delivery project (projectId, public id). A deal has one project, of the same client (409 deal_has_project, 400 deal_project_other_client); linking the same pair again changes nothing. If the project is already running (active) and the deal still open, the deal is marked won. Returns wonDeal.

Does what the dashboard's linkObjectAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
projectId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/link-project' \  -H 'Content-Type: application/json' \  -d '{  "projectId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/unlink-project

Removes the link between this deal and the project (projectId, public id). Neither record's status changes — a deal won because its project started stays won. Unlinking a pair that was never linked does nothing.

Does what the dashboard's unlinkObjectAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
projectId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/unlink-project' \  -H 'Content-Type: application/json' \  -d '{  "projectId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/set-planned-project

Sets the existing project (projectId, public id; null clears it) this deal joins when it is won: winning it then creates no project, links the deal to that one and adds a client-visible roadmap milestone named after the deal. The project must be live delivery (active or proposal, else 400 deal_planned_project_closed) of the deal's own client (400 deal_project_other_client); a deal that already has its project answers 409 deal_has_project. Read back as plannedProjectId.

Does what the dashboard's setDealPlannedProjectAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
projectId*|null

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-planned-project' \  -H 'Content-Type: application/json' \  -d '{  "projectId": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/link-invoice

Attaches an invoice of the agency's Pennylane mirror to the deal it bills: pennylaneId (Pennylane's id) or invoiceId (as projects › deals returns it), exactly one. Only an invoice of the deal's own client (400 invoice_deal_other_client); a quote or an unknown id answers 404. An invoice attached to another deal moves to this one; attaching it again changes nothing (changed: false). Nothing is written in Pennylane.

Does what the dashboard's linkDealInvoiceAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
pennylaneId?integer
Range0 < value <= 9007199254740991
invoiceId?string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/link-invoice' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/unlink-invoice

Detaches an invoice (pennylaneId or invoiceId, exactly one) from this deal. An invoice attached to another deal, or to none, is left alone (changed: false). Nothing is written in Pennylane.

Does what the dashboard's unlinkDealInvoiceAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
pennylaneId?integer
Range0 < value <= 9007199254740991
invoiceId?string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/unlink-invoice' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/set-recurrence-amount

From effectiveFrom (YYYY-MM-DD) on, the recurring deal bills amountCents per period (excl. tax) (e.g. +200 € a month from March) without a new deal. One step per day: the same day again changes its amount (changed: false when it already was). A step overrides the deal's base amount from its day on. A one-shot deal answers 409 deal_not_recurring.

Does what the dashboard's setRecurrenceAmountAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
effectiveFrom*string
Match^\d{4}-\d{2}-\d{2}$
amountCents*integer
Range0 <= value <= 2000000000

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-recurrence-amount' \  -H 'Content-Type: application/json' \  -d '{  "effectiveFrom": "string",  "amountCents": 0}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/remove-recurrence-amount

Removes the step of effectiveFrom (YYYY-MM-DD); changed: false when the deal had none that day. The amount then in force is the previous step's, else the deal's base amount.

Does what the dashboard's removeRecurrenceAmountAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
effectiveFrom*string
Match^\d{4}-\d{2}-\d{2}$

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/remove-recurrence-amount' \  -H 'Content-Type: application/json' \  -d '{  "effectiveFrom": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/add-option

The "+ Option → new recurring deal" gesture: creates a small recurring deal of the same client and period, on the agency's follow-up pipeline (no new onboarding), planned to join this deal's project at its win when that project is live, named name (default <deal> — option), worth amountCents per period. For an option that needs its own quote and signature; a change of amount on this deal is set-recurrence-amount. A one-shot deal answers 409 deal_not_recurring. Returns the new deal's public id.

Does what the dashboard's addRecurringOptionAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
name?string
Length1 <= length <= 200
amountCents?|null

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/add-option' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
GET
/deals/{id}/actions/to-invoice

What this recurring deal is due in the month, what is already invoiced and what is missing; an empty rows when it is not won, not recurring, not started or ended. Amounts are excl. tax: dueCents (the amount in force; a quarterly, half-yearly or yearly deal is due in full in the months its period starts, counted from its start), invoicedCents (its attached invoices issued that month, excl. tax) and missingCents (never below zero). month is YYYY-MM, this month by default. Read-only: nothing is written to Pennylane.

Reads the same data as the in-app listToInvoice - same implementation. Changes nothing; also answers GET with the same fields as query parameters.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Query Parameters

month?string
Match^\d{4}-(0[1-9]|1[0-2])$

Response Body

The figures.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X GET 'https://api.usedashly.com/v1/deals/string/actions/to-invoice'
Example Response
{  "data": null}
POST
/deals/{id}/actions/to-invoice

What this recurring deal is due in the month, what is already invoiced and what is missing; an empty rows when it is not won, not recurring, not started or ended. Amounts are excl. tax: dueCents (the amount in force; a quarterly, half-yearly or yearly deal is due in full in the months its period starts, counted from its start), invoicedCents (its attached invoices issued that month, excl. tax) and missingCents (never below zero). month is YYYY-MM, this month by default. Read-only: nothing is written to Pennylane.

Reads the same data as the in-app listToInvoice - same implementation. Changes nothing; also answers GET with the same fields as query parameters.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
month?string
Match^\d{4}-(0[1-9]|1[0-2])$

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/to-invoice' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
GET
/deals/actions/to-invoice-all

Every won recurring deal of the agency due or invoiced in the month, the largest missing amount first, with the totals (what the Today page's invoicing widget shows). Amounts are excl. tax: dueCents (the amount in force; a quarterly, half-yearly or yearly deal is due in full in the months its period starts, counted from its start), invoicedCents (its attached invoices issued that month, excl. tax) and missingCents (never below zero). month is YYYY-MM, this month by default. Read-only: nothing is written to Pennylane.

Reads the same data as the in-app listToInvoice - same implementation. Changes nothing; also answers GET with the same fields as query parameters.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Query Parameters

month?string
Match^\d{4}-(0[1-9]|1[0-2])$

Response Body

The figures.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X GET 'https://api.usedashly.com/v1/deals/actions/to-invoice-all'
Example Response
{  "data": null}
POST
/deals/actions/to-invoice-all

Every won recurring deal of the agency due or invoiced in the month, the largest missing amount first, with the totals (what the Today page's invoicing widget shows). Amounts are excl. tax: dueCents (the amount in force; a quarterly, half-yearly or yearly deal is due in full in the months its period starts, counted from its start), invoicedCents (its attached invoices issued that month, excl. tax) and missingCents (never below zero). month is YYYY-MM, this month by default. Read-only: nothing is written to Pennylane.

Reads the same data as the in-app listToInvoice - same implementation. Changes nothing; also answers GET with the same fields as query parameters.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
month?string
Match^\d{4}-(0[1-9]|1[0-2])$

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/actions/to-invoice-all' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}

Split a deal into parts

POST
/deals/{id}/actions/split

Splits this deal into parts in one transaction: its project is reused (the planned one, else the linked one) or created as a proposal for the deal's client, one roadmap milestone per one-shot row, one deal per ticked row. Row 0 may be this deal itself (isSource: true, its kind kept; its milestone reused). Each row of rows (1 to 20) is a part: name, kind (one_shot | recurring), amountCents (excl. tax; per period when recurring), createDeal (true: a deal now on the follow-up pipeline; false: a planned milestone holding the amount as its estimate for a one-shot row, nothing stored yet for a recurring row, returned in skipped). A one-shot row is a roadmap milestone: startsOn, endsOn (YYYY-MM-DD) and precision (day | month | quarter | half) together, or none. A recurring row takes period (default monthly), start ({kind: on_win}, {kind: date, on} or {kind: after_row, row} naming a one-shot row of the same body by its index) and commitmentMonths. New deals belong to the same client and contact, are planned to join the project, and are expected to close when their milestone (or their recurrence) starts. When the agency has several follow-up pipelines, name one by its public id in followUpPipelineId (refusal follow_up_pipeline_required lists them); with none, the refusal is no_follow_up_pipeline. roadmapClientVisible: true (the 'show the roadmap to the client' box) writes the milestones visible in the client's roadmap; absent or false, they stay internal until the agency shows them. The same idempotencyKey sent again answers the first result (replayed: true); with another body it answers 409 split_key_reused. Needs projects:write as well. Returns the project, deals and milestones by public id.

Does what the dashboard's splitIntoPartsAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
rows*array<>
Items1 <= items <= 20
followUpPipelineId?|null
idempotencyKey*string
Match^[A-Za-z0-9_.:-]{8,255}$
roadmapClientVisible?boolean|null

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/split' \  -H 'Content-Type: application/json' \  -d '{  "rows": [    {      "name": "string",      "kind": "one_shot",      "createDeal": true    }  ],  "idempotencyKey": "string"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/invite-portal-member

Invites email into the client portal of this deal's organisation, whether it is still a prospect or already a client: the same portal, with the same sections and visibility rules. Writes a pending invitation (as POST /v1/portal-invitations does) and mails its link to a signed client's people only: while Dashly sends no mail to leads and prospects (owner decision 2026-10-22, the prospectEmails switch), an organisation that is not a signed client (prospect, lost) is NOT mailed, and the response carries the join link in invitationUrl for the agency to send itself (mailed: false); a client's answer has mailed: true and invitationUrl: null. roleKey is client_member (default) or client_admin, and name names the person when the address is new to the organisation (the CRM person is found or created, never duplicated). A deal without organisation answers 422 portal_deal_no_client. Needs clients:write as well. Returns the client by its public id, the invitation's address, role and status, mailed and invitationUrl.

Does what the dashboard's invitePortalAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
email*string
Match^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
Formatemail
Lengthlength <= 200
roleKey?string
Default"client_member"
Value in"client_member""client_admin"
name?string
Lengthlength <= 120

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/invite-portal-member' \  -H 'Content-Type: application/json' \  -d '{  "email": "user@example.com"}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/revoke-portal-access

Closes the client portal of this deal's organisation once EVERY deal of it is lost and it is still a prospect: in one transaction, every portal member is removed and every pending invitation cancelled (their logins and CRM records stay). Refused with 409 portal_revoke_deal_open while a deal of the organisation is open or won, 409 portal_revoke_not_prospect for a signed client (closed member by member on clients), 422 portal_deal_no_client for a deal without organisation. Running it again revokes nothing more. Needs clients:write as well. Returns the client by its public id and the addresses removed and cancelled.

Does what the dashboard's revokeDealPortalAccessAction does - same implementation, so the two cannot diverge. Takes no body.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/revoke-portal-access'
Example Response
{  "data": null}
GET
/deals/{id}/actions/portal-visibility

Answers whether this deal is shown in its organisation's client portal (visible, false until the agency switches it on) and whether its amounts are hidden there (hideAmount). installed: false while the feature's migration is not applied (the deal is then hidden). Read only.

Reads the same data as the in-app setDealPortalVisibilityAction - same implementation. Changes nothing; also answers GET with the same fields as query parameters. Takes no body.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Response Body

The figures.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X GET 'https://api.usedashly.com/v1/deals/string/actions/portal-visibility'
Example Response
{  "data": null}
POST
/deals/{id}/actions/portal-visibility

Answers whether this deal is shown in its organisation's client portal (visible, false until the agency switches it on) and whether its amounts are hidden there (hideAmount). installed: false while the feature's migration is not applied (the deal is then hidden). Read only.

Reads the same data as the in-app setDealPortalVisibilityAction - same implementation. Changes nothing; also answers GET with the same fields as query parameters. Takes no body.

Required scope: deals:read.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/portal-visibility'
Example Response
{  "data": null}
POST
/deals/{id}/actions/set-portal-visibility

Shows this deal in its organisation's client portal (visible: true) or hides it (false, the default for every deal): once shown, the organisation's members holding portal.deals.read see its project's roadmap — each part with a client-readable status (under discussion, accepted, declined; never the pipeline stage), the recurring parts with their start, the recap, the client-visible next steps and the team on the project. hideAmount: true leaves every amount and the recap out of what the portal receives. Both switches are written together. A deal without organisation answers 422 portal_deal_no_client; before the feature's migration, 409 deal_portal_not_installed. Needs clients:write as well. Returns the deal by its public id and both switches.

Does what the dashboard's setDealPortalVisibilityAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
visible*boolean
hideAmount?boolean
Defaultfalse

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-portal-visibility' \  -H 'Content-Type: application/json' \  -d '{  "visible": true}'
Example Response
{  "data": null}
POST
/deals/{id}/actions/move-to-pipeline

Moves the deal to another sales pipeline. The destination stage is required and must belong to the target pipeline - stages do not map from one pipeline to another.

Does what the dashboard's moveDealToPipelineAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
pipelineId*string
Length1 <= length <= 60
stageId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/move-to-pipeline' \  -H 'Content-Type: application/json' \  -d '{  "pipelineId": "string",  "stageId": "string"}'
Example Response
{  "data": null}

Set the primary contact

POST
/deals/{id}/actions/set-primary-contact

Makes this contact the deal's primary contact, adding them to the buying committee if they were not on it. Their weight on the committee is left alone.

Does what the dashboard's setDealPrimaryContactAction does - same implementation, so the two cannot diverge.

Required scope: deals:write.

Authorization

bearerAuth
headerAuthorizationBearer <token>

Your agency's API key: Authorization: Bearer ak_…. Created in Connectors → API & webhooks and shown once. A key authenticates an agency, not a person.

Path Parameters

id*string

The public id of the record to act on. It is resolved inside your agency before the action runs, so an id from another tenant is a 404 and never reaches it.

Header Parameters

Idempotency-Key?string

A value you choose (a UUID is the usual one), 8 to 255 characters of A-Z a-z 0-9 _ . : -. Retrying with the same key returns the first call's answer instead of creating a second record, for seven days. The same key with a different body returns 409.

Length8 <= length <= 255

Request Body

application/json
  1. body
contactId*string
Length1 <= length <= 60

Response Body

Done.

application/json
  1. response
data*unknown

The action's result; its shape depends on the verb.

Example Request
curl -X POST 'https://api.usedashly.com/v1/deals/string/actions/set-primary-contact' \  -H 'Content-Type: application/json' \  -d '{  "contactId": "string"}'
Example Response
{  "data": null}