Dashly Docs

Projects

`/projects`

List projects

GET
/projects

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: projects: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"-createdAt"
Value in"createdAt""-createdAt""name""-name""status""-status"
q?string

Case-insensitive "contains", across: name.

filter[status]?string

Exact match on status.

filter[clientId]?string

Exact match on clientId.

filter[parentId]?string

Exact match on parentId.

Response Body

The requested page.

application/json
  1. response
data*array<>
meta*
Example Request
curl -X GET 'https://api.usedashly.com/v1/projects'
Example Response
{  "data": [    {      "id": "string",      "clientId": "string",      "parentId": "string",      "name": "string",      "description": "string",      "status": "string",      "recurrencePeriod": "monthly",      "startsOn": "2019-08-24",      "endsOn": "2019-08-24",      "dealId": "string",      "stagingUrls": null,      "siteUrl": "string",      "isPoc": true,      "createdAt": "2019-08-24T14:15:22Z"    }  ],  "meta": {    "page": 0,    "perPage": 0,    "total": 0,    "totalPages": 0  }}

Create a project

POST
/projects

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: projects: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
description?string|null
status?string
Lengthlength <= 40
clientId?string|null
projectTypeId?string|null
recurrencePeriod?|null
stagingUrls?array<>
Itemsitems <= 10
isPoc?boolean
parentId?string|null
siteUrl?|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/projects' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "parentId": "string",    "name": "string",    "description": "string",    "status": "string",    "recurrencePeriod": "monthly",    "startsOn": "2019-08-24",    "endsOn": "2019-08-24",    "dealId": "string",    "stagingUrls": null,    "siteUrl": "string",    "isPoc": true,    "createdAt": "2019-08-24T14:15:22Z"  }}

Retrieve a project

GET
/projects/{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: projects: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/projects/string'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "parentId": "string",    "name": "string",    "description": "string",    "status": "string",    "recurrencePeriod": "monthly",    "startsOn": "2019-08-24",    "endsOn": "2019-08-24",    "dealId": "string",    "stagingUrls": null,    "siteUrl": "string",    "isPoc": true,    "createdAt": "2019-08-24T14:15:22Z"  }}

Update a project

PATCH
/projects/{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: projects: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
description?string|null
status?string
Lengthlength <= 40
clientId?string|null
projectTypeId?string|null
recurrencePeriod?|null
stagingUrls?array<>
Itemsitems <= 10
isPoc?boolean
parentId?string|null
siteUrl?|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/projects/string' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": {    "id": "string",    "clientId": "string",    "parentId": "string",    "name": "string",    "description": "string",    "status": "string",    "recurrencePeriod": "monthly",    "startsOn": "2019-08-24",    "endsOn": "2019-08-24",    "dealId": "string",    "stagingUrls": null,    "siteUrl": "string",    "isPoc": true,    "createdAt": "2019-08-24T14:15:22Z"  }}

Delete a project

DELETE
/projects/{id}

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

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

List available actions

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

Share with a partner

POST
/projects/{id}/actions/share-with-partner

Shares this project with an already-linked partner agency. Idempotent: re-sharing reactivates a revoked share. allowWidget explicitly grants or revokes widget access; omitting it preserves the previous permission.

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

Required scope: projects: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
partnerOrgId*string
Length1 <= length
allowWidget?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/projects/string/actions/share-with-partner' \  -H 'Content-Type: application/json' \  -d '{  "partnerOrgId": "string"}'
Example Response
{  "data": null}

Revoke a share

POST
/projects/{id}/actions/revoke-share

Removes a partner's access to this shared project: a partner agency by partnerOrgId, or a partner of /v1/partners (an independent or a company) by partnerId. Name exactly one.

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

Required scope: projects: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
partnerOrgId?string
Length1 <= length
partnerId?string
Length1 <= length

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

Invite a person by email

POST
/projects/{id}/actions/invite-person

Invites an outside person onto this project by email - creating the CRM person, the partner and the seat when needed - and shares the project with them. Idempotent on the address: re-running refreshes the invitation link. company makes that agency (found by name or created) the partner, instead of the person as an independent. allowTime is only honoured on a new share. allowWidget explicitly grants or revokes widget access, including on existing shares; omitting it preserves the previous permission.

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

Required scope: projects: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
name?string
Lengthlength <= 120
allowTime?boolean
allowWidget?boolean
company?string
Length2 <= length <= 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/projects/string/actions/invite-person' \  -H 'Content-Type: application/json' \  -d '{  "email": "user@example.com"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/add-member

Adds an agency member to this project, optionally with their role on it (free text, 80 characters). memberId is the member's user id, the one the members resource returns. Already assigned: nothing changes, except the role when one is given.

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

Required scope: projects: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
memberId*string
Length1 <= length
role?string
Lengthlength <= 80

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/projects/string/actions/add-member' \  -H 'Content-Type: application/json' \  -d '{  "memberId": "string"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/set-member-role

Sets what an agency member does on this project (role, free text, 80 characters; null or "" clears it). The member must already be on the project.

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

Required scope: projects: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
memberId*string
Length1 <= length
role*|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/projects/string/actions/set-member-role' \  -H 'Content-Type: application/json' \  -d '{  "memberId": "string",  "role": "string"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/remove-member

Removes an agency member from this project. memberId is the member's user id. Not assigned does nothing.

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

Required scope: projects: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
memberId*string
Length1 <= length

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/projects/string/actions/remove-member' \  -H 'Content-Type: application/json' \  -d '{  "memberId": "string"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/set-dates

Sets the project's planned start and end (startsOn, endsOn: YYYY-MM-DD, or null for none). The end may not precede the start. Read back as startsOn / endsOn on the project.

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

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

Set the project's cycles

POST
/projects/{id}/actions/set-cycles

Turns cycles on or changes them — weeks (1-4) and anchorOn (YYYY-MM-DD, the earliest day the next cycle may start) — creating the current cycle and the two after it; or turns them off with weeks: null (the cycles and their tickets stay).

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

Required scope: projects: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
weeks*|null
anchorOn?string|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/projects/string/actions/set-cycles' \  -H 'Content-Type: application/json' \  -d '{  "weeks": -9007199254740991}'
Example Response
{  "data": null}
GET
/projects/{id}/actions/deals

The deals of this project — planned on it, linked to it, or selling one of its roadmap milestones — each with its amount excl. tax (per period when recurring), kind, status, stage, milestone, linked Pennylane quotes and attached invoices; the planned milestones (no deal yet) with their estimate; and the totals: oneShotSignedCents (won), oneShotPipeCents (open, unweighted), oneShotPlannedCents (estimates), mrrCents (won recurring, per month) totalValueCents (all of them, recurring over 12 months), invoicedCents (invoices attached to won one-shot deals, excl. tax) and leftToInvoiceCents (each won one-shot block's amount minus its invoices). A deal's milestone.amountCents is the block's own amount when one was confirmed at the win (null = the deal's amount), and it is what the totals count. Lost deals are listed but counted nowhere. Needs deals:read as well. Read-only.

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

Required scope: projects: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/projects/string/actions/deals'
Example Response
{  "data": null}
POST
/projects/{id}/actions/deals

The deals of this project — planned on it, linked to it, or selling one of its roadmap milestones — each with its amount excl. tax (per period when recurring), kind, status, stage, milestone, linked Pennylane quotes and attached invoices; the planned milestones (no deal yet) with their estimate; and the totals: oneShotSignedCents (won), oneShotPipeCents (open, unweighted), oneShotPlannedCents (estimates), mrrCents (won recurring, per month) totalValueCents (all of them, recurring over 12 months), invoicedCents (invoices attached to won one-shot deals, excl. tax) and leftToInvoiceCents (each won one-shot block's amount minus its invoices). A deal's milestone.amountCents is the block's own amount when one was confirmed at the win (null = the deal's amount), and it is what the totals count. Lost deals are listed but counted nowhere. Needs deals:read as well. Read-only.

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

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

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

Add parts to a project

POST
/projects/{id}/actions/add-parts

Adds parts to this project in one transaction (no row may be isSource): one roadmap milestone per one-shot row, one deal of the project's client per ticked row. 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 deals:write as well. Returns the deals and milestones by public id.

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

Required scope: projects: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/projects/string/actions/add-parts' \  -H 'Content-Type: application/json' \  -d '{  "rows": [    {      "name": "string",      "kind": "one_shot",      "createDeal": true    }  ],  "idempotencyKey": "string"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/merge

Merges the listed projects (sourceIds, public ids) into this one, in one transaction: their tickets (with this project's client), milestones, documents, deals (each deal is kept and now points here), time, billing, chat, portal items, members, teams and partner shares, relations and history move here; what this project already has (a member, a team, a share, an SEO checklist, a widget site) and the sources' cycles stay on the sources, which are archived, never deleted. A source of another client needs confirmClientChange: true; widenDates widens this project's dates to cover theirs; dryRun: true answers the counts (moved, kept) and conflicts without writing. Re-running the same merge changes nothing (alreadyMerged).

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

Required scope: projects: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
sourceIds*array<>
Items1 <= items <= 50
confirmClientChange?boolean
dryRun?boolean
widenDates?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/projects/string/actions/merge' \  -H 'Content-Type: application/json' \  -d '{  "sourceIds": [    "string"  ]}'
Example Response
{  "data": null}

Read the project's stack

GET
/projects/{id}/actions/stack

The catalogue ids of the services this project runs on (Webflow, Vercel, Supabase…), in the order they were linked. Read-only.

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

Required scope: projects: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/projects/string/actions/stack'
Example Response
{  "data": null}

Read the project's stack

POST
/projects/{id}/actions/stack

The catalogue ids of the services this project runs on (Webflow, Vercel, Supabase…), in the order they were linked. Read-only.

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

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

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

linked: true puts a catalogue service on the project's stack (and, for a client project, on the client's services); linked: false takes it off the stack only. Idempotent. Answers the stack.

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

Required scope: projects: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
serviceId*string
linked*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/projects/string/actions/set-service' \  -H 'Content-Type: application/json' \  -d '{  "serviceId": "string",  "linked": true}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/apply-stack-template

Links the services of a stack template the project does not run on yet; never unlinks one. Templates: webflow-site, framer-site, shopify-store, wordpress-site, web-app, seo, ads. Answers the stack.

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

Required scope: projects: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
templateId*string
Length1 <= length <= 64

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/projects/string/actions/apply-stack-template' \  -H 'Content-Type: application/json' \  -d '{  "templateId": "string"}'
Example Response
{  "data": null}
GET
/projects/{id}/actions/repositories

The repositories linked to this project with what GitHub last said about each (default branch, last commits, open pull requests, latest release or tag, CI status of the default branch, open issue count — cached, refreshed by webhooks and every few hours), and the pull requests linked to the project's tickets (a ticket's public id in the PR's title or branch, or AGD- anywhere). Read-only.

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

Required scope: projects: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/projects/string/actions/repositories'
Example Response
{  "data": null}
POST
/projects/{id}/actions/repositories

The repositories linked to this project with what GitHub last said about each (default branch, last commits, open pull requests, latest release or tag, CI status of the default branch, open issue count — cached, refreshed by webhooks and every few hours), and the pull requests linked to the project's tickets (a ticket's public id in the PR's title or branch, or AGD- anywhere). Read-only.

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

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

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/projects/string/actions/repositories' \  -H 'Content-Type: application/json' \  -d '{}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/link-repository

Links owner/repo (or its URL) to the project and reads it from GitHub at once — through the agency's GitHub App when installed, anonymously otherwise (public repositories only). 409 already_linked, 409 too_many_repositories past 10. Answers the repositories.

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

Required scope: projects: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
repository*string
Length3 <= length <= 300

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/projects/string/actions/link-repository' \  -H 'Content-Type: application/json' \  -d '{  "repository": "string"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/unlink-repository

Takes a linked repository (its id from repositories) off the project. The repository itself and the tickets' links are untouched. Answers the repositories.

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

Required scope: projects: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
repositoryId*string
Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

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/projects/string/actions/unlink-repository' \  -H 'Content-Type: application/json' \  -d '{  "repositoryId": "5aa3e9fa-2d0a-4c21-ad54-43217e2bc9c0"}'
Example Response
{  "data": null}
POST
/projects/{id}/actions/refresh-repository

Reads one linked repository from GitHub now instead of waiting for the next webhook or poll. A refusal from GitHub is stored as the repository's syncError, the last snapshot kept. Answers the repositories.

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

Required scope: projects: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
repositoryId*string
Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

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/projects/string/actions/refresh-repository' \  -H 'Content-Type: application/json' \  -d '{  "repositoryId": "5aa3e9fa-2d0a-4c21-ad54-43217e2bc9c0"}'
Example Response
{  "data": null}
POST
/projects/{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: projects: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/projects/string/actions/link-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}
POST
/projects/{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: projects: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/projects/string/actions/unlink-object' \  -H 'Content-Type: application/json' \  -d '{  "targetType": "account",  "targetId": "string"}'
Example Response
{  "data": null}