Dashly Docs

Errors

The error envelope of the Dashly API, its codes, and the precise reasons that tell you which rule refused a call.

Every failure has the same shape, whatever the endpoint:

{
  "error": {
    "code": "validation_failed",
    "message": "A required field is empty",
    "details": { "field": "budget", "reason": "field_required" },
    "request_id": "req_6f1c2b9e4d7a4c0e9b3f5a8d2e1c7b40"
  }
}
FieldMeaning
codeThe error class. A fixed list, mapped one-to-one to an HTTP status.
messageA technical description in English, for developers and logs.
detailsStructured context, when there is any: the reason, the field at fault, the accepted values, a limit.
request_idThe id of the request, also sent in the X-Request-Id header. Quote it to support.

Branch on codes, not messages

message is written for humans and may change at any time. It often names the value in cause, and its wording is improved without notice. Never parse it.

Your code should branch on:

  1. error.code, for the class of problem (and the HTTP status);
  2. error.details.reason, for the exact rule that refused the call, when present.
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
if (!res.ok) {
  const { error } = await res.json();
  if (error.code === "rate_limited") {
    // wait Retry-After seconds, then retry
  } else if (error.details?.reason === "scope_missing") {
    // ask for a key with error.details.required
  } else if (error.details?.reason === "plan_required") {
    // the agency's plan lacks error.details.feature
  }
}

Codes and reasons are part of the API contract: new ones can be added, existing ones are never renamed.

Error codes

error.codeHTTP status
unauthorized401
forbidden403
not_found404
invalid_request400
validation_failed422
payload_too_large413
method_not_allowed405
conflict409
rate_limited429
server_error500
  • 400 invalid_request: the request itself is malformed (an unknown query parameter, a filter or sort the resource does not support, invalid JSON, a body that is not an object, a bad Idempotency-Key). details lists what is accepted when that helps.
  • 401 unauthorized: no key, an unknown key, or a revoked or expired key. See Authentication.
  • 403 forbidden: the key is valid but may not do this. details.reason says why (missing scope, plan, suspended key…).
  • 404 not_found: the resource, the record or the action does not exist for this agency. Records of other agencies are indistinguishable from records that do not exist.
  • 409 conflict: the current state forbids the action, or an Idempotency-Key was reused (see Idempotency).
  • 413 payload_too_large: the body is over 1 MiB; details.limit is the limit in bytes.
  • 422 validation_failed: the request is well-formed but a value is refused.
  • 429 rate_limited: too many requests, or the monthly quota is spent. Always comes with Retry-After. See Rate limits and quota.
  • 500 server_error: a problem on our side. It is recorded automatically; retry later and quote the request_id if it persists.

Refusal reasons

When a business rule refuses a call, details.reason names it, next to the unchanged code. The table below lists every reason with its class and a short meaning.

details.reasonClassMeaning
agency_memory_fact_refusedvalidationThis fact cannot be kept in the assistant's memory
agency_memory_fullconflictThe assistant's memory is full
agency_memory_not_installedconflictAssistant memory not installed
already_existsconflictThe name is already in use
already_linkedconflictRepository already linked to the project
already_memberconflictThe person is already a member
ambiguous_namevalidationSeveral items match this name
assistant_client_rate_limitedrate_limitToo many messages to the assistant, retry later
assistant_conversation_title_invalidvalidationThe conversation title must be between 1 and 120 characters
assistant_conversations_not_installedconflictAssistant conversations not installed
assistant_exchange_limitrate_limitToo many actions asked in one message
assistant_instructions_limit_reachedconflictAssistant instruction limit reached
assistant_instructions_not_installedconflictAssistant instructions not installed
assistant_reply_withheldvalidationThe assistant's reply was withheld
assistant_ticket_duplicateconflictA request with the same content was opened recently
assistant_ticket_rate_limitedrate_limitToo many requests opened by the assistant for this client, retry later
automation_limit_reachedconflictAutomation rule limit reached
bot_not_in_channelconflictThe bot is not in the channel
brief_needs_clientvalidationThe opportunity has no organisation to file the brief under
channel_archivedconflictThe channel is archived
channel_not_foundnot_foundSlack channel not found
chat_files_limit_reachedconflictToo many files on one message
client_assignees_not_installedconflictClient assignees not installed
client_has_no_projectsconflictThe client has no project
client_reminders_not_installedconflictClient reminders not installed
client_visibility_requiredconflictAssigning the client makes the ticket visible in their portal; confirm it
column_name_requiredvalidationA column has no name
conflictconflictThe current state no longer allows this action
connection_expiredconflictThe service connection is no longer valid
connector_cannot_issue_keyauthzA connector cannot create API keys
consent_requiredvalidationThis service connects by consent, not by key
content_requiredvalidationThe step content is required
crawl_credits_insufficientconflictNot enough crawl credits left
crawl_not_runningconflictNo crawl is running
crawl_runningconflictA crawl is already running for this client
credits_exhaustedconflictNot enough credits left this month
cycle_other_projectvalidationThe cycle belongs to another project
deal_deck_not_providerconflictA deal's presentation is never shown to providers
deal_has_milestoneconflictThe deal already carries another milestone
deal_has_projectconflictThe deal is already linked to another project
deal_not_recurringconflictThe deal is not recurring
deal_planned_project_closedvalidationThe project is not live delivery
deal_portal_not_installedconflictDeal portal visibility not installed
deal_project_other_clientvalidationThe deal and the project belong to different clients
deck_notes_not_installedconflictSpeaker notes not installed
deletion_disabledauthzDeleting records is disabled for this agency
dns_record_missingconflictDNS verification record not found
doc_access_client_onlyvalidationAccess to clients and providers is set on a client's document
doc_briefs_not_installedconflictAttaching documents to the pipe is not installed
doc_client_has_childrenconflictThis document has sub-pages
doc_comments_not_installedconflictDocument comments not installed
doc_has_commentsconflictThe document has comments
doc_project_no_clientvalidationA provider's document is written on a client's project
doc_provider_not_on_agencyvalidationThis provider works on none of the agency's projects
doc_provider_not_on_clientvalidationThis provider does not work on this client
domain_not_verifiedconflictDomain not verified for the agency (TXT record missing)
duplicate_column_keyvalidationTwo columns share the same key
duplicate_field_keyvalidationTwo form fields share the same key
embed_host_not_allowedvalidationEmbed host is not on the allowlist
field_requiredvalidationA required field is empty
file_emptyvalidationFile is empty
file_missingvalidationFile is missing
file_too_largevalidationFile is too large
file_type_blockedvalidationFile type refused (web page, SVG or script)
follow_up_pipeline_requiredvalidationChoose the follow-up pipeline of the new deals
forbiddenauthzNot allowed
form_fields_invalidvalidationA form field is invalid (key or label)
form_fields_missingvalidationA form step needs at least one field
geo_not_readyconflictAI visibility module not ready (disabled, no engine or no tracked question)
github_token_invalidvalidationGitHub refused the access token
github_unavailableupstreamGitHub is unavailable
identity_requiredvalidationAn organisation or a person is required
image_not_foundnot_foundNo usable image at this address
image_type_unsupportedvalidationUnsupported image format
impersonation_refusedauthzImpersonation refused for this account
internalinternalInternal error
internal_deck_client_projectvalidationAn internal presentation cannot be filed in a client's project
internal_deck_not_portalconflictAn internal presentation cannot be shown in a client portal
internal_item_client_projectvalidationAn internal work item cannot be filed in a client's project
invalid_api_keyvalidationAPI key rejected by the remote service
invalid_inputvalidationInvalid input
invalid_repositoryvalidationNot a GitHub repository reference
invalid_stateconflictThe current state does not allow this action
invalid_statusvalidationInvalid status
invitation_not_pendingconflictThe invitation is no longer pending
invite_pendingconflictThe address already has a pending invitation
invoice_deal_other_clientvalidationThe invoice belongs to another client than the deal
item_project_not_sharedconflictThis item's project is not shared with any provider
key_not_attachedauthzThe API key is not attached to a member
key_suspendedauthzThe API key is suspended after unusual activity
limit_reachedconflictLimit reached
llm_output_invalidupstreamThe model's answer was unreadable
member_integrations_not_installedconflictPersonal integrations are not installed
member_linkedin_takenconflictThis LinkedIn account is already connected by another member
mention_internal_clientvalidationMention of a client in an internal note
mention_unknownvalidationMention of a person without access to the ticket
merge_already_mergedconflictA record was already merged into another one
merge_ancestorvalidationThe kept ticket is a subtask of a merged ticket
merge_client_mismatchconflictA merged record belongs to another client: confirm the change
merge_selfvalidationA record cannot be merged into itself
milestone_deal_other_clientvalidationThe deal belongs to another client
milestone_has_dealconflictThe milestone already has its deal
milestone_not_plannedconflictThe milestone has no period to postpone
milestone_other_clientvalidationThe milestone belongs to another client
milestone_other_projectvalidationThe milestone belongs to another project
name_requiredvalidationName is required
no_follow_up_pipelineconflictThe agency has no follow-up pipeline
no_open_columnvalidationEvery column marks work as finished
no_terminal_columnvalidationNo column marks work as finished
not_a_client_membervalidationThe person is not a member of this ticket's client portal
not_a_membervalidationThe person is not a member of the agency
not_connectedconflictThe service is not connected
not_foundnot_foundNot found
notification_events_not_installedconflictPer-event notification settings not installed
onboarding_lockedconflictThe onboarding can no longer be edited
origin_invalidvalidationOrigin refused
owner_protectedconflictThe agency owner cannot be changed or removed
partner_has_active_sharesconflictThe partner still has shared projects: confirm they are closed too
partner_has_dealsconflictThe partner is on a deal and cannot be deleted
partner_name_requiredvalidationThe partner needs a name
partner_not_linkedvalidationThe agency is not a linked partner
periodic_reports_not_installedconflictPeriodic reports not installed
permission_missingauthzThe member lacks the required permission
phone_invalidvalidationInvalid phone number (E.164 expected)
phone_takenconflictPhone number already assigned to another member
pipe_other_clientvalidationThe opportunity does not belong to this document's client
plan_requiredauthzThe agency's plan does not include this feature
platform_admin_requiredauthzOnly a platform administrator may do this
portal_already_memberconflictThe address is already a member of this space
portal_deal_no_clientvalidationThe deal has no client organisation
portal_invitations_limit_reachedconflictToo many pending invites
portal_last_adminconflictThe space's last admin cannot be removed
portal_member_selfconflictA portal member cannot change or remove themselves
portal_quick_links_limit_reachedconflictQuick link limit reached
portal_replacement_invalidvalidationThe replacement admin must be another member of this space
portal_revoke_deal_openconflictEvery deal of the client must be lost before its portal access is revoked
portal_revoke_not_prospectconflictOnly a prospect's portal access is revoked from a deal
portal_role_escalationauthzA portal member cannot grant a role above their own
portal_setup_pendingconflictFinish the space onboarding first
pptx_emptyvalidationImport produced no usable content
pptx_no_slidesvalidationNo slide found in the PPTX file
pptx_unreadablevalidationPPTX file is unreadable
private_key_unreadablevalidationUnreadable private key (PEM)
project_other_clientvalidationThe project does not belong to this client
prompt_limit_reachedconflictTracked-question limit reached
provider_assistant_cap_invalidvalidationThe monthly cap per provider must be a whole number between 0 and 500
query_filter_fullvalidationThe query filter is full
query_filter_name_takenconflictA query filter has this name
query_filter_too_slowvalidationThe query filter took too long
query_filters_not_installedconflictQuery filters not installed
rate_limitedrate_limitToo many attempts, retry shortly
recurrence_end_before_startvalidationThe recurrence ends before it starts
recurrence_milestone_missingvalidationA start after a milestone needs its milestone
recurrence_milestone_other_clientvalidationThe milestone belongs to another client than the deal
recurrence_start_missingvalidationA start on a date needs its date
repositories_not_installedconflictLinking repositories to a project is not installed
request_closedconflictThe request is closed
required_steps_missingvalidationRequired steps are still incomplete
resources_not_installedconflictResources table not installed
review_access_refusedvalidationReview access list refused (admin role removed or person outside client/agency)
review_origin_missingconflictNo review domain configured
review_site_people_limit_reachedconflictReview site people limit
reviewer_not_membervalidationThe person does not belong to the agency or the chosen client
roadmap_not_installedconflictThe roadmap is not installed
scope_missingauthzThe API key lacks the required scope
scope_too_broadauthzA key cannot grant scopes it does not hold
seat_limit_reachedconflictSeat limit reached
select_options_missingvalidationA select field has no options
sentry_issue_invalidvalidationNot a Sentry issue reference
sentry_issues_not_installedconflictSentry issues not installed
sentry_org_not_foundvalidationUnknown Sentry organisation
sentry_token_invalidvalidationSentry refused the auth token
sentry_unavailableupstreamSentry is unavailable
seo_checklist_not_installedconflictSEO checklist not installed
services_not_installedconflictFollowing a client's services is not installed
social_already_publishedconflictThe post is already published
social_body_invalidvalidationEmpty text or too long for the network
social_date_requiredvalidationA publication date is required
social_media_limitvalidationToo many media, or media that cannot mix
social_media_too_largevalidationThe file is too large for the network
social_media_typevalidationThis network does not take this file type
social_not_installedconflictSocial module not installed
social_zernio_account_invalidvalidationUnknown Zernio account, or not on the author's network
social_zernio_key_invalidvalidationZernio refused the API key
social_zernio_unavailableupstreamZernio is unavailable
social_zernio_unconfiguredconflictZernio is not connected
split_key_reusedconflictThis idempotency key was used for another split
split_no_clientvalidationThe deal or project has no client
split_source_kind_mismatchvalidationThe current deal's row must keep the deal's kind
split_source_not_dealvalidationOnly a deal being split has a 'current deal' row
stage_has_dealsconflictThe stage still holds deals
storage_not_configuredinternalStorage is not configured
storage_unavailableupstreamStorage is unavailable
sub_project_nestedvalidationSub-projects are one level deep
sub_project_other_clientvalidationA sub-project has the client of its global project
sub_project_selfvalidationA project cannot be its own sub-project
system_folder_lockedconflictA client's folder cannot be renamed or deleted
tag_limit_reachedconflictAutomation tag limit reached
ticket_has_no_clientconflictThe ticket belongs to no client
ticket_other_clientvalidationThe work item does not belong to this client
timer_not_runningconflictNo timer is running
too_longvalidationText is too long
too_many_addressesvalidationToo many widget addresses
too_many_repositoriesconflictToo many repositories on the project
unauthenticatedauthzNot authenticated
upload_failedupstreamFile upload failed
upstream_rejectedupstreamThe remote service rejected the request
upstream_unavailableupstreamThe remote service is unavailable
url_not_allowedvalidationURL not allowed: only public HTTPS addresses are accepted
website_missingvalidationThe client has no website on record
weekly_bilan_not_installedconflictThe weekly summary is not installed
whatsapp_filing_rate_limitedrate_limitToo many requests filed on WhatsApp for this client, retry later
workflow_invalidvalidationInvalid workflow (missing columns, unnamed column, or no terminal / non-terminal column)
workflow_too_few_columnsvalidationA workflow needs two columns
wrong_passwordauthzWrong password

Some reasons are documented with the feature they belong to rather than in this table: the 429 reasons (burst_limit, write_burst_limit, ip_limit, auth_failures, volume_budget, agency_quota_exhausted, user_quota_exhausted) in Rate limits and quota, the 409 reasons idempotency_key_conflict and idempotency_key_in_flight in Idempotency, and feature_disabled (a 404 for a collection that is switched off on the platform, with details.feature).

Common details

details.field

When a single field is at fault, details.field names it, as the key you sent (endAt, projectId, or the key of a custom field):

{
  "error": {
    "code": "validation_failed",
    "message": "Invalid input",
    "details": { "field": "endAt", "reason": "invalid_input" },
    "request_id": "req_…"
  }
}

When the body does not match the resource's schema at all (a wrong type, a missing required field, an unknown field), details is instead a list, one entry per problem:

{
  "error": {
    "code": "validation_failed",
    "message": "Invalid input: expected string, received undefined",
    "details": [
      { "field": "name", "message": "Invalid input: expected string, received undefined" }
    ],
    "request_id": "req_…"
  }
}

details.feature with plan_required

A feature the agency's plan does not include is refused with 403 forbidden, details.reason: "plan_required", and details.feature, the id of the missing feature or add-on (for example chat, portal_branding, seo or rank_tracking). Use it to point the agency to the right upgrade instead of parsing the message.

{
  "error": {
    "code": "forbidden",
    "message": "The sitemap tool requires the Pro plan or above.",
    "details": { "feature": "seo", "reason": "plan_required" },
    "request_id": "req_…"
  }
}

details.max and details.count with limits

A refusal because a ceiling is reached (limit_reached and the more specific *_limit_reached reasons) carries details.max, the ceiling, and details.count, the current number, when it is known:

{
  "error": {
    "code": "validation_failed",
    "message": "This client already tracks 50 questions, the maximum. …",
    "details": { "max": 50, "count": 50, "reason": "limit_reached" },
    "request_id": "req_…"
  }
}

Accepted values

A 400 about a query parameter lists what the resource accepts: accepted, filterable or sortable. A 403 scope_missing lists required and granted. See Requests and responses and Authentication.

On this page