Proposals
`/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.
bearerAuthAuthorizationBearer <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.
page?integer1-based. meta.totalPages says how many there are.
1 <= value1per_page?integerRows per page. A value above 200 returns 400 - never a silent clamp.
1 <= value <= 20050sort?stringPrefix with - for descending. An undeclared sort returns 400.
"-updatedAt""updatedAt""-updatedAt""createdAt""-createdAt""title""-title""status""-status"q?stringCase-insensitive "contains", across: title, clientName.
filter[status]?stringExact match on status.
"draft""sent""viewed""accepted""declined""paid"filter[locale]?stringExact match on locale.
The requested page.
application/json- response
data*array<>meta*curl -X GET 'https://api.usedashly.com/v1/proposals'{ "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 }}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.
bearerAuthAuthorizationBearer <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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
title*string1 <= length <= 200document?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"fr""en"clientName?|nullcurrency?string3 <= length <= 3amountCents?|nullforfaitType?|nullforfaitMinutes?|nulldealId?string|nullprojectId?string|nullcreatedAt?stringWhen this actually happened, for an import. Past dates only; defaults to now. updatedAt follows it unless the body sets one.
date-timeCreated. The body is the new record, as a read would return it.
application/json- response
data*curl -X POST 'https://api.usedashly.com/v1/proposals' \ -H 'Content-Type: application/json' \ -d '{ "title": "string"}'{ "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" }}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.
bearerAuthAuthorizationBearer <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.
id*stringThe record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.
Found.
application/json- response
data*curl -X GET 'https://api.usedashly.com/v1/proposals/string'{ "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" }}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.
bearerAuthAuthorizationBearer <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.
id*stringThe record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.
application/json- body
title?string1 <= length <= 200document?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"fr""en"clientName?|nullcurrency?string3 <= length <= 3amountCents?|nullforfaitType?|nullforfaitMinutes?|nulldealId?string|nullprojectId?string|nullUpdated. The body is the record as it now stands.
application/json- response
data*curl -X PATCH 'https://api.usedashly.com/v1/proposals/string' \ -H 'Content-Type: application/json' \ -d '{}'{ "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" }}Permanent. The audit log keeps the trace; the record does not come back.
Required scope: proposals:write.
bearerAuthAuthorizationBearer <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.
id*stringThe record's public id (hsbftmp3f5), as returned in the id field. An internal UUID returns 404.
Deleted. Empty body.
curl -X DELETE 'https://api.usedashly.com/v1/proposals/string'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.
bearerAuthAuthorizationBearer <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.
The available verbs.
application/json- response
data*array<>curl -X GET 'https://api.usedashly.com/v1/proposals/actions'{ "data": [ { "name": "string", "label": "string", "description": "string", "target": "item", "readOnly": true, "path": "string", "input": {} } ]}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/new-version' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
clientName?|nulllocale*string2 <= length <= 12currency*string3 <= length <= 3publicSlug?|nullDone.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/update-settings' \ -H 'Content-Type: application/json' \ -d '{ "locale": "string", "currency": "str"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
theme*|nullDone.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/set-theme' \ -H 'Content-Type: application/json' \ -d '{ "theme": { "property1": null, "property2": null }}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
password*stringlength <= 200Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/set-password' \ -H 'Content-Type: application/json' \ -d '{ "password": "string"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
enabled*booleanDone.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/set-sharing' \ -H 'Content-Type: application/json' \ -d '{ "enabled": true}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
fileName*string1 <= length <= 200contentType*string^image\/length <= 120data*string^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$base64length <= 6990508Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/actions/upload-image' \ -H 'Content-Type: application/json' \ -d '{ "fileName": "string", "contentType": "string", "data": "string"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
enableStripe?booleanstickyPay?booleanbankTransfer?|nullDone.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/set-payment-settings' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
name*string1 <= length <= 200scope?string"theme""document"Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/actions/publish-template' \ -H 'Content-Type: application/json' \ -d '{ "name": "string"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
name*string1 <= length <= 200authorName?stringlength <= 200Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/actions/install-marketplace-template' \ -H 'Content-Type: application/json' \ -d '{ "name": "string"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
targetType*string"account""contact""project""ticket""deal""user""proposal"targetId*string1 <= length <= 60Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/link-object' \ -H 'Content-Type: application/json' \ -d '{ "targetType": "account", "targetId": "string"}'{ "data": null}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.
bearerAuthAuthorizationBearer <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.
id*stringThe 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.
Idempotency-Key?stringA 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.
8 <= length <= 255application/json- body
targetType*string"account""contact""project""ticket""deal""user""proposal"targetId*string1 <= length <= 60Done.
application/json- response
data*unknownThe action's result; its shape depends on the verb.
curl -X POST 'https://api.usedashly.com/v1/proposals/string/actions/unlink-object' \ -H 'Content-Type: application/json' \ -d '{ "targetType": "account", "targetId": "string"}'{ "data": null}