Skip to Content
Conventions

Conventions

Base URL

https://api.dokaa.app/v1

HTTPS only. All responses are JSON with snake_case field names.

Authentication

Pass your API key on every request:

Authorization: Bearer dk_live_...

Each key carries scopes that gate which endpoints it can call, and a perimeter: the shops it can read. Requests outside the key’s scopes return 403; unknown or revoked keys return 401.

Scopes

Every key carries scopes that gate which endpoints it can call; enforcement is server-side, on every request. Keys created today carry every read scope. Fine-grained scope selection at creation is planned; the table below shows which scope unlocks which endpoint.

ScopeEndpoints it unlocks
shops:read/shops
reviews:read/reviews, /analytics/reviews
feedbacks:read/analytics/feedbacks
contacts:read/contacts, /analytics/contacts
campaigns:read/campaigns
missed_calls:read/analytics/missed-calls
wheel_games:read/analytics/wheel-games
audiences:read/audiences
google_interactions:read/google-interactions
google_posts:read/google-posts, /google-post-groups, /google-post-groups/eligible-shops, /google-post-groups/{batch_id}
keyword_tracking:read/keyword-tracking, /keyword-rankings/summary, /keyword-rankings/trend
competitors:read/competitors, /competitors/positioning, /competitors/evolution
dashboard:read/summary

A key’s scopes cannot be extended after creation. Its shop perimeter follows its definition: a key created for a group covers the shops that later join that group.

Shop perimeter

  • On multi-shop endpoints, omit shop_id to query all the shops your key can access at once, or pass it to focus on a single shop.
  • Competitor and keyword tracking endpoints are single-shop: shop_id is required on /competitors, /competitors/positioning, /competitors/evolution, /keyword-tracking, /keyword-rankings/summary and /keyword-rankings/trend.
  • A shop_id outside your perimeter returns 403.
  • GET /shops lists your perimeter and the ids to use.

Pagination

List endpoints are cursor-paginated:

{ "data": [ ... ], "pagination": { "next_cursor": "eyJkIjoiMjAyNi0w...", "has_more": true } }

Pass pagination.next_cursor back as ?cursor= to fetch the next page, until has_more is false. Cursors are opaque: do not build or modify them. The default page size is 25, and ?limit= accepts 1 to 100.

Cursor pagination is stable: items created while you paginate can never shift your pages, so exports get every row exactly once.

Dates

  • All timestamps are returned in UTC, ISO 8601 (2026-08-01T09:30:00.000Z).
  • from / to filters accept ISO dates (2026-01-01). to is inclusive of the whole day.

Errors

Errors follow RFC 9457 Problem Details  with Content-Type: application/problem+json. The type URI identifies the error kind; its last segment is the stable identifier to match on:

{ "type": "https://api.dokaa.app/docs/errors/invalid-parameters", "title": "Invalid parameters", "status": 422, "detail": "\"from\" must be in ISO 8601 date format" }
StatusTypeMeaning
401missing-api-key / invalid-api-keyNo key, unknown key, or revoked key
403missing-scopeThe key lacks the scope this endpoint requires
403shop-access-deniedThe requested shop_id is outside the key’s perimeter
404resource-not-foundThe requested resource does not exist or holds no data (unknown batch id, no competitor or ranking snapshot)
422invalid-parametersA query parameter failed validation
422invalid-cursorThe pagination cursor is malformed
500internal-errorUnexpected error on our side

Versioning

  • Additive changes (new endpoints, new optional fields, new enum values) ship continuously and are always safe: your integration must tolerate unknown fields.
  • Breaking changes are exceptional and ship under a new base path (/v1 → /v2), announced in advance in the changelog. /v1 keeps working unchanged: you migrate when you choose to.

Every change is listed in the changelog.