NoteBugsDocs

Integration

Recipes

Whole flows, from the token to concluding an epic.

Build a board from scratch

Workspace, project, label, epic and the first card, in the order the dependencies require.

export API=http://localhost:3000/api
export TOKEN="the token from My account → Security"
auth=(-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json")

# 1. the workspace, born with the 5 default columns
TENANT=$(curl -s "${auth[@]}" -X POST $API/tenants \
  -d '{ "name": "Support", "color": "sky" }' | jq -r .id)

# 2. the entry column, from the workspace set
COLUMN=$(curl -s "${auth[@]}" "$API/columns?tenant=$TENANT" | jq -r '.[0].id')

# 3. project and label
PROJECT=$(curl -s "${auth[@]}" -X POST $API/projects \
  -d "{ \"tenantId\": \"$TENANT\", \"name\": \"Integrations\" }" | jq -r .id)

TAG=$(curl -s "${auth[@]}" -X POST $API/tags \
  -d "{ \"tenantId\": \"$TENANT\", \"name\": \"api\" }" | jq -r .id)

# 4. the epic, which inherits the project's workspace
EPIC=$(curl -s "${auth[@]}" -X POST $API/epics \
  -d "{ \"projectId\": \"$PROJECT\", \"name\": \"Onboarding\" }" | jq -r .id)

# 5. the card
curl -s "${auth[@]}" -X POST $API/cards \
  -d "{
    \"tenantId\": \"$TENANT\",
    \"title\": \"Publish the documentation\",
    \"columnId\": \"$COLUMN\",
    \"projectId\": \"$PROJECT\",
    \"epicId\": \"$EPIC\",
    \"tagIds\": [\"$TAG\"]
  }"

Move a card and read the history

# afterCardId null drops it at the top of the destination column
curl -s "${auth[@]}" -X POST $API/cards/$CARD/move \
  -d "{ \"columnId\": \"$OTHER_COLUMN\", \"afterCardId\": null }"

# the column change became a history row
curl -s "${auth[@]}" $API/cards/$CARD/history

Edit in bulk without losing labels

# in a batch, labels are ADD/REMOVE and not the final list,
# so the labels each card already had are not erased
curl -s "${auth[@]}" -X PATCH $API/cards/bulk \
  -d "{
    \"cardIds\": [\"$C1\", \"$C2\", \"$C3\"],
    \"branch\": \"release/2026-09\",
    \"addTagIds\": [\"$TAG\"]
  }"

Set a due date that means the same day for everyone

// The two fields travel together: the instant, and the zone it was
// DECLARED in. Without `dueAtZone`, an 11pm due date in Sao Paulo would
// show up the next day for whoever reads it in UTC.
await api(`/cards/${cardId}`, {
  method: "PATCH",
  body: JSON.stringify({
    dueAt: "2026-10-15T23:59:00.000Z",
    dueAtZone: "America/Sao_Paulo",
  }),
});

// In a PATCH, an absent `dueAtZone` PRESERVES the stored zone.
// To clear the due date, send an explicit null:
await api(`/cards/${cardId}`, {
  method: "PATCH",
  body: JSON.stringify({ dueAt: null }),
});

Conclude an epic that still has pending cards

# without deciding the pending cards, this is 422
curl -s "${auth[@]}" -X PATCH $API/epics/$EPIC \
  -d '{ "status": "DONE" }'

# conclude the pending cards where they are…
curl -s "${auth[@]}" -X PATCH $API/epics/$EPIC \
  -d '{ "status": "DONE", "pendingCards": "DONE" }'

# …or send what is left into the next epic
curl -s "${auth[@]}" -X PATCH $API/epics/$EPIC \
  -d "{
    \"status\": \"DONE\",
    \"pendingCards\": \"MOVE\",
    \"pendingEpicId\": \"$NEXT\"
  }"

Attach a file

# multipart: with no hand-written Content-Type, curl writes the boundary
curl -s -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  $API/cards/$CARD/attachments

# the response carries the url ready for Markdown
# [screenshot](/api/files/18ab694d-….png)

Make a backup and wait for it

curl -s "${auth[@]}" -X POST $API/backup/job \
  -d '{ "destination": "LOCAL" }'

# exporting is a JOB: follow it until it leaves PENDING/RUNNING
until [ "$(curl -s "${auth[@]}" $API/backup/job | jq -r .status)" = "READY" ]; do
  sleep 2
done

curl -s "${auth[@]}" -o notebugs-backup.zip $API/backup/file