Dashly Docs

Proposals

`/proposals`

List proposals

GET
/proposals

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: proposals: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"updatedAt""-updatedAt""createdAt""-createdAt""title""-title""status""-status"
q?string

Case-insensitive "contains", across: title, clientName.

filter[status]?string

Exact match on status.

Value in"draft""sent""viewed""accepted""declined""paid"
filter[locale]?string

Exact match on locale.

Response Body

The requested page.

application/json
  1. response
data*array<>
meta*
Example Request
curl -X GET 'https://api.usedashly.com/v1/proposals'
Example Response
{  "data": [    {      "id": "string",      "title": "string",      "status": "draft",      "document": null,      "locale": "string",      "clientName": "string",      "currency": "string",      "amountCents": 0,      "forfaitType": "monthly",      "forfaitMinutes": 0,      "viewCount": 0,      "createdAt": "2019-08-24T14:15:22Z",      "updatedAt": "2019-08-24T14:15:22Z"    }  ],  "meta": {    "page": 0,    "perPage": 0,    "total": 0,    "totalPages": 0  }}

Create a proposal

POST
/proposals

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: proposals: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
title*string
Length1 <= length <= 200
document?array<unknown>

The BlockNote document (block array). Besides the stock blocks it takes: toc (table of contents from the headings), projectRoadmap, dealQuote (prop scope: all_one_shot or a deal's public id), recurring and financialSummary. Those four read the project's deals while the version is a draft; when it is sent (status leaves draft, or sharing is turned on) Dashly writes what they show into their snapshot prop, which must then be sent back unchanged. The printable page (browser PDF, agency members only) is /proposals/{id}/print in the app.

locale?string
Value in"fr""en"
clientName?|null
currency?string
Length3 <= length <= 3
amountCents?|null
forfaitType?|null
forfaitMinutes?|null
dealId?string|null
projectId?string|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/proposals' \  -H 'Content-Type: application/json' \  -d '{  "title": "string"}'
Example Response
{  "data": {    "id": "string",    "title": "string",    "status": "draft",    "document": null,    "locale": "string",    "clientName": "string",    "currency": "string",    "amountCents": 0,    "forfaitType": "monthly",    "forfaitMinutes": 0,    "viewCount": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z"  }}

Retrieve a proposal

GET
/proposals/{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: proposals: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/proposals/string'
Example Response
{  "data": {    "id": "string",    "title": "string",    "status": "draft",    "document": null,    "locale": "string",    "clientName": "string",    "currency": "string",    "amountCents": 0,    "forfaitType": "monthly",    "forfaitMinutes": 0,    "viewCount": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z"  }}

Update a proposal

PATCH
/proposals/{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: proposals: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
title?string
Length1 <= length <= 200
document?array<unknown>

The BlockNote document (block array). Besides the stock blocks it takes: toc (table of contents from the headings), projectRoadmap, dealQuote (prop scope: all_one_shot or a deal's public id), recurring and financialSummary. Those four read the project's deals while the version is a draft; when it is sent (status leaves draft, or sharing is turned on) Dashly writes what they show into their snapshot prop, which must then be sent back unchanged. The printable page (browser PDF, agency members only) is /proposals/{id}/print in the app.

locale?string
Value in"fr""en"
clientName?|null
currency?string
Length3 <= length <= 3
amountCents?|null
forfaitType?|null
forfaitMinutes?|null
dealId?string|null
projectId?string|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/proposals/string' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": {    "id": "string",    "title": "string",    "status": "draft",    "document": null,    "locale": "string",    "clientName": "string",    "currency": "string",    "amountCents": 0,    "forfaitType": "monthly",    "forfaitMinutes": 0,    "viewCount": 0,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z"  }}

Delete a proposal

DELETE
/proposals/{id}

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

Required scope: proposals: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/proposals/string'
Example Response
Empty

List available actions

GET
/proposals/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: proposals: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/proposals/actions'
Example Response
{  "data": [    {      "name": "string",      "label": "string",      "description": "string",      "target": "item",      "readOnly": true,      "path": "string",      "input": {}    }  ]}

Create the next version

POST
/proposals/{id}/actions/new-version

Duplicates the current proposal as version N+1 - document, theme, terms - and leaves the old public link redirecting to it. The new version starts as a draft: its dynamic blocks read the project's live data again.

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

Required scope: proposals: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

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/proposals/string/actions/new-version' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}

Update the settings

POST
/proposals/{id}/actions/update-settings

Saves the proposal's settings: the client name shown, the language of the public page, the currency and the public slug.

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

Required scope: proposals: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
clientName?|null
locale*string
Length2 <= length <= 12
currency*string
Length3 <= length <= 3
publicSlug?|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/proposals/string/actions/update-settings' \  -H 'Content-Type: application/json' \  -d '{  "locale": "string",  "currency": "str"}'
Example Response
{  "data": null}

Set the theme

POST
/proposals/{id}/actions/set-theme

Applies a visual theme to the proposal's public page - colours, block styling - or null to inherit the agency's branding.

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

Required scope: proposals: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
theme*|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/proposals/string/actions/set-theme' \  -H 'Content-Type: application/json' \  -d '{  "theme": {    "property1": null,    "property2": null  }}'
Example Response
{  "data": null}

Set the password

POST
/proposals/{id}/actions/set-password

Protects the proposal's public page with a password, stored hashed. An empty password removes the protection.

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

Required scope: proposals: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
password*string
Lengthlength <= 200

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/proposals/string/actions/set-password' \  -H 'Content-Type: application/json' \  -d '{  "password": "string"}'
Example Response
{  "data": null}

Turn sharing on or off

POST
/proposals/{id}/actions/set-sharing

Turns the proposal's public link on - minting a share token and returning the URL - or off, revoking the token. Turning it on sends the version: its dynamic blocks are frozen in the same write.

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

Required scope: proposals: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
enabled*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/proposals/string/actions/set-sharing' \  -H 'Content-Type: application/json' \  -d '{  "enabled": true}'
Example Response
{  "data": null}

Upload an image

POST
/proposals/actions/upload-image

Uploads an image for the content blocks (base64, 5 MB maximum) and returns the public delivery URL to reference from an image block.

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

Required scope: proposals: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
fileName*string
Length1 <= length <= 200
contentType*string
Match^image\/
Lengthlength <= 120
data*string
Match^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$
Formatbase64
Lengthlength <= 6990508

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/proposals/actions/upload-image' \  -H 'Content-Type: application/json' \  -d '{  "fileName": "string",  "contentType": "string",  "data": "string"}'
Example Response
{  "data": null}

Set the payment options

POST
/proposals/{id}/actions/set-payment-settings

Saves the proposal's payment options: the Stripe payment button on the public page, the button pinned at the bottom, and bank transfer details (null to remove them).

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

Required scope: proposals: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
enableStripe?boolean
stickyPay?boolean
bankTransfer?|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/proposals/string/actions/set-payment-settings' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
POST
/proposals/actions/publish-template

Publishes one of the agency's templates - theme or document, named by its exact name - to the cross-agency marketplace. The agency's name is shown as the author.

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

Required scope: proposals: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
scope?string
Value in"theme""document"

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/proposals/actions/publish-template' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
Example Response
{  "data": null}
POST
/proposals/actions/install-marketplace-template

Copies a template published on the marketplace into the agency's template library, and bumps its install count. Name it by its exact name, plus authorName when several share it.

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

Required scope: proposals: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
authorName?string
Lengthlength <= 200

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/proposals/actions/install-marketplace-template' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
Example Response
{  "data": null}
POST
/proposals/{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: proposals: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/proposals/string/actions/link-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}
POST
/proposals/{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: proposals: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/proposals/string/actions/unlink-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}