Aller au contenu

Build your first integration

Ce contenu n’est pas encore disponible dans votre langue.

The API reference tells you what each endpoint does. This page shows you how to put four of them together into something useful.

Everything below is plain curl, so it translates to any language.

  1. Settings → MCP → API keys → New key. See API keys and tokens.

    Terminal window
    export DOTBY_TOKEN="dotby_pat_..."
  2. Always start here. me tells you your user, your workspaces, their ids and slugs, your role in each, and each one’s plan.

    Terminal window
    curl -H "Authorization: Bearer $DOTBY_TOKEN" \
    https://api.dotby.app/v1/me
  3. You can use human identifiers anywhere an id is expected — reads and writes.

    Thing Use
    Workspace slug or id — acme
    Project key or id — ENG
    Task KEY-N or id — ENG-42

    So /v1/issues/ENG-42 works. You rarely need to look up an id.

The classic: a failed build opens a bug.

Tasks are created under a project, and the project can be named by its key:

Terminal window
curl -X POST \
https://api.dotby.app/v1/workspaces/acme/projects/ENG/issues \
-H "Authorization: Bearer $DOTBY_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: build-4821-failure" \
-d '{
"title": "Build 4821 failed on main",
"description": "Nightly build failed. See the run log."
}'

A good key is something the calling system already has and will reuse on retry: a build number, a webhook delivery id, a commit SHA.

Every list endpoint returns the same envelope:

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

Page until has_more is false:

Terminal window
cursor=""
while :; do
page=$(curl -s -H "Authorization: Bearer $DOTBY_TOKEN" \
"https://api.dotby.app/v1/workspaces/acme/issues?limit=100&cursor=$cursor")
echo "$page" | jq -c '.data[]'
[ "$(echo "$page" | jq -r .has_more)" = "true" ] || break
cursor=$(echo "$page" | jq -r .next_cursor)
done

limit defaults to 25 and maxes at 100.

Do not conclude something is absent from one page. Filters can thin a page without ending the list, so an empty page with has_more: true is normal. Page to the end before deciding.

Recipe 3 — sync cheaply with delta polling

Section titled “Recipe 3 — sync cheaply with delta polling”

Crawling everything on a schedule is wasteful. Crawl once, then poll for what changed:

Terminal window
curl -H "Authorization: Bearer $DOTBY_TOKEN" \
"https://api.dotby.app/v1/workspaces/acme/issues?updatedAfter=1770000000000"

updatedAfter takes epoch milliseconds. Store the timestamp of your last successful sync and pass it back next time.

It returns only tasks meaningfully edited strictly after that time. Page until next_cursor is null, then record your poll time.

One caveat worth knowing: legacy rows that have never been edited since update-stamping began are excluded. Do one full crawl to seed your store, and use updatedAfter for increments only.

Recipe 4 — batch create with per-item results

Section titled “Recipe 4 — batch create with per-item results”

Importing a list? Create up to 100 tasks in one call:

Terminal window
curl -X POST https://api.dotby.app/v1/workspaces/acme/issues/batch \
-H "Authorization: Bearer $DOTBY_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: import-2026-03-04" \
-d '{
"projectId": "ENG",
"items": [
{ "title": "Migrate the auth service" },
{ "title": "Update the runbook" }
]
}'

The response splits results per item, under HTTP 200:

{ "succeeded": [{ "index": 0, "...": "..." }], "failed": [{ "index": 1, "problem": {} }] }

One bad row never poisons the other ninety-nine — which also means a 200 does not mean everything worked. Always read failed.

Errors are RFC 9457 problem+json with a machine-stable code. Branch on the code, never on the message.

Status Code What to do
401 unauthenticated, invalid_token Token is wrong or revoked. Do not retry.
403 forbidden, insufficient_scope The owner lacks the right. Do not retry.
403 upgrade_required Workspace is on Free. Do not retry.
404 not_found_or_forbidden Missing or invisible to you — deliberately indistinguishable.
409 idempotency_conflict Same key, different body. Fix the key.
429 rate_limited Wait Retry-After seconds, then retry.
400 validation_failed Bad input. Do not retry.

Only 429 is worth retrying automatically.

The whole surface is described by an OpenAPI 3.1 document:

Terminal window
curl https://api.dotby.app/v1/openapi.json

Feed it to your generator of choice and skip hand-writing request types. The docs are generated from the same source, so the spec cannot drift from behavior.

Two shortcuts that are often the right answer:

  • The CLI already wraps all of this — --json, a built-in --jq, and exit codes you can branch on in a shell script.
  • MCP gives an AI agent the same operations with no code at all.