Dashly Docs

Requests and responses

Base URLs, response envelopes, public ids, pagination, filtering, sorting, search and the discovery endpoints of the Dashly API.

Base URL

https://api.usedashly.com/v1

The same API is also mounted under the application at https://app.usedashly.com/api/v1. Both behave identically; prefer api.usedashly.com.

Requests and responses are JSON. Send Content-Type: application/json with every request that has a body.

Operations

Every collection (clients, contacts, deals, tickets…) follows the same pattern:

MethodPathDoesSuccess
GET/v1/{resource}List records200
POST/v1/{resource}Create a record201
GET/v1/{resource}/{id}Read one record200
PATCH/v1/{resource}/{id}Update the fields you send200
DELETE/v1/{resource}/{id}Delete the record204

Some collections are read-only, and some cannot be deleted; the catalogue (below) lists the methods each one accepts. A method a collection does not accept answers 405 method_not_allowed.

Request bodies are strict: a field the resource does not accept is refused with 422 validation_failed, never silently ignored. PATCH only changes the fields present in the body, with one exception, customFields, covered in Custom fields.

Beyond create, update and delete, many operations are actions, such as converting a lead or importing a CSV. See Actions.

Envelopes

A single record comes back in data:

{ "data": { "id": "hsbftmp3f5", "name": "Boudbois", "status": "active" } }

A list comes back in data, with pagination in meta:

{
  "data": [{ "id": "hsbftmp3f5", "name": "Boudbois", "status": "active" }],
  "meta": { "page": 1, "perPage": 50, "total": 128, "totalPages": 3 }
}

An error comes back in error. See Errors.

{
  "error": {
    "code": "not_found",
    "message": "No clients item \"x9x9x9x9x9\".",
    "request_id": "req_6f1c2b9e4d7a4c0e9b3f5a8d2e1c7b40"
  }
}

Ids are public ids

Every id the API returns or accepts is a short public id such as hsbftmp3f5. Custom fields have ids of the form fld_…. Use public ids everywhere, in paths and in relation fields:

curl -X POST https://api.usedashly.com/v1/contacts \
  -H "Authorization: Bearer $DASHLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "Claire", "lastName": "Martin", "clientId": "hsbftmp3f5" }'

An id that is unknown, or that belongs to another agency, is answered the same way: 404 in a path, 422 with details.reason: "not_found" in a body.

Pagination

Lists are paginated by page number.

ParameterMeaningDefault
pagePage to return, starting at 11
per_pageRecords per page, at most 20050

A per_page above 200 is refused with 400 invalid_request rather than silently capped, so you never skip records without knowing. Use meta.totalPages to know when to stop:

curl "https://api.usedashly.com/v1/deals?page=2&per_page=200" \
  -H "Authorization: Bearer $DASHLY_API_KEY"

Pagination is offset-based. If records are created or deleted while you page through a collection, a record can move between pages. Sort on a stable field such as createdAt for long syncs.

Sorting

sort takes one field name, prefixed with - for descending order:

?sort=-createdAt
?sort=name

Each resource declares its sortable fields and its default order. Clients, for example, sort on createdAt, name and status, newest first by default.

q runs a case-insensitive "contains" search over the resource's searchable fields (for clients: name and sector):

?q=bakery

Filters

filter[field]=value keeps the records whose field equals the value. Several filters combine with AND:

curl "https://api.usedashly.com/v1/deals?filter[status]=open&filter[clientId]=hsbftmp3f5" \
  -H "Authorization: Bearer $DASHLY_API_KEY"

Values are checked against the field's type: a boolean takes true or false, a number must be numeric, and a field with a fixed list of values only accepts one of them.

Unknown parameters are errors

An unknown query parameter, a field that cannot be filtered or sorted on, or a value outside a field's list answers 400 invalid_request, and details lists what is accepted. A typo never returns the whole unfiltered collection.

{
  "error": {
    "code": "invalid_request",
    "message": "Filter \"stage\" is not supported.",
    "details": { "filterable": ["status", "clientId"] },
    "request_id": "req_0b8e3a1f9c2d4e6f8a7b5c3d1e9f0a2b"
  }
}

Request ids

Every response carries an X-Request-Id header, and every error envelope repeats it as request_id. Quote it when you contact support.

You can send your own id in X-Request-Id (8 to 128 characters from A-Z a-z 0-9 _ . : -). Dashly then uses yours, so you can find a call under the id your own logs already use. Anything else is replaced by a generated id of the form req_….

Body size

A request body is limited to 1 MiB. A larger body is refused with 413 payload_too_large before anything is read or written; details.limit gives the limit in bytes.

Discovery

Three endpoints describe the API and your agency. Any valid key can call them, whatever its scopes.

GET /v1: the catalogue

Lists every resource and its contract: readable and writable fields, relations, filters, sorts, searchable fields, actions, custom field entity and scopes.

curl https://api.usedashly.com/v1 \
  -H "Authorization: Bearer $DASHLY_API_KEY"
{
  "data": {
    "version": "v1",
    "resources": [
      {
        "name": "clients",
        "label": "Clients",
        "path": "/v1/clients",
        "methods": ["GET", "POST", "PATCH", "DELETE"],
        "attributes": ["id", "name", "slug", "sector", "website", "linkedinUrl", "siren", "status", "locale", "createdAt"],
        "writable": ["name", "slug", "sector", "website", "linkedinUrl", "siren", "status", "locale"],
        "relations": [],
        "filterable": ["status", "sector", "lead"],
        "actions": ["import-csv", "site-statistics", "ai-visibility" /* … */],
        "sortable": ["createdAt", "name", "status"],
        "searchable": ["name", "sector"],
        "customFields": "account",
        "backdatable": true,
        "scopes": ["clients:read", "clients:write"]
      }
      // … every other resource
    ]
  }
}

customFields names the entity whose custom fields apply to the resource (see Custom fields). backdatable: true means a POST may carry a createdAt, so records imported from another tool keep their original date.

GET /v1/me: the key

Returns the key's agency, its scopes and the agency's remaining monthly quota. Call it at startup to validate the key without reading any record.

{
  "data": {
    "agency": { "name": "Northwind Studio", "slug": "northwind-studio" },
    "scopes": ["*:read"],
    "quota": {
      "used": 1240,
      "limit": 50000,
      "remaining": 48760,
      "unlimited": false,
      "resetsAt": "2026-11-01"
    }
  }
}

On an unlimited plan, limit and remaining are null and unlimited is true.

GET /v1/fields: the field registry

Lists the fields of each entity, built-in and custom. Add ?entity=account|deal|lead|ticket|contact to get one entity. See Custom fields.

Identifying your client

You may send X-Dashly-Surface: <name> (lowercase letters, digits, _ and -, up to 20 characters) to label your integration in the agency's activity history. The header grants nothing; it only helps the agency tell which tool made a change.

On this page