Skip to content
Docs
About By Dawn Contact supportOpen By Dawn
  1. Get started
  2. Authentication

Authentication

To call the API you need two things. An API key is created once in By Dawn and lives on your server. An access token is what your code gets from that key and sends with every request. This page explains how the two fit together, what a key may do, and how to look after keys.

The split exists for safety. A key lasts a long time, so it never travels with ordinary requests. A token expires after about 15 minutes, so one that leaks into a log can’t do much harm.

An organization owner or admin creates keys in By Dawn, under Settings › Organization › Security, on the API keys card (API keys walks through it). Give the key a name you’ll recognise later, pick what it may do (see Scopes), and choose when it expires: in 30, 90 or 365 days, or never. By Dawn shows the key once, so copy it straight away. It starts with saw_live_.

Keep the key on a server, in a secret store or a CI secret. Don’t put it in a web page or a mobile app, where anyone using them could read it. (Pasting your own key into Try it on this site is fine, because it never leaves your browser.)

Your code sends the key to the accounts host and gets a token back:

POST https://accounts.dev.sawrun.com/api/v1/auth/token/api-key
Content-Type: application/json
{ "apiKey": "saw_live_…" }
{ "ok": true, "data": { "accessToken": "eyJ…", "tokenType": "Bearer", "expiresInSeconds": 900 } }

Then it sends the token with every request to the workspace host, in an Authorization header:

GET https://workspace.dev.sawrun.com/api/v1/orgs/{orgId}/projects
Authorization: Bearer eyJ…

Keep using the same token until it’s about to expire, then exchange again. Don’t exchange before every request: the exchange has its own, lower rate limit. A long-running integration should remember when its token expires (expiresInSeconds from now) and swap a minute before then.

If a request comes back 401 unauthorized, the token has expired or its key was revoked. Exchange once more and repeat the request. If that fails too, stop, because the key itself no longer works.

Every workspace path starts with /orgs/{orgId}. A key belongs to exactly one organization, and you don’t need to look its ID up: the access token carries it, in its org_id claim. The token is a JWT, so you can read the claim by decoding its middle part:

quickstart.ts (excerpt)
// The token is a JWT; its `org_id` claim is the organization the key belongs to.
const claims = JSON.parse(atob(token.data.accessToken.split('.')[1]!.replace(/-/g, '+').replace(/_/g, '/')));
const orgId: string = claims.org_id;

Asking about any other organization gets 403 forbidden.

When you create a key you choose its Access, and that decides what its tokens may do:

Access in Settings Scopes What it allows
Board — read only tickets:read Read tickets, comments, projects, columns, labels, cycles, epics and the teams that work in a project
Board — read & write tickets:read, tickets:write Everything above, plus create, update, move and archive tickets, post comments, link tickets together, and create, update and close epics
Tests — submit results tests:write Send test runs from CI (Send test results from CI), and nothing else: it can’t read or change tickets
Tests — read cases & runs tests:read Read a project’s test case library, its runs and their results, and nothing on the board
Tests — read & write cases & runs tests:read, tests:write Everything above, plus create and edit test cases, folders and manual runs, import cases, and send test runs from CI

Separately from its Access, a key has a Customers access, which opens your organization’s customers (Customers API):

Customers in Settings Scopes What it allows
None none Nothing about customers. This is the default
Read customers:read Read customers, their requests, and the customer statuses and tiers
Read & write customers:read, customers:write Everything above, plus create and update customers, and add, change and remove the requests on a ticket. Changing a ticket’s requests also needs tickets:read on its project

Customers only in Access makes a key with no board access, for a sync that only keeps your customer list up to date.

Start with read only, and make a read & write key when your integration needs to change things. Give a CI job that only sends test results a Tests key, so a leaked CI secret can’t touch your board. A request the key’s scopes don’t cover gets 403 forbidden.

A key acts for your organization, not for a person, and it’s kept away from a few things a person should decide. So whatever its scopes, a key can’t:

  • list tickets across the whole organization. Pass a projectId when listing tickets. For the same reason it can’t list a team’s tickets across projects (GET /teams/{teamId}/tickets), so filter each project by teamId instead, and it must pass a projectId when listing epics (GET /epics).
  • list the organization’s members. GET /users answers 403 forbidden, so assign tickets with user IDs you already have.
  • list the organization’s teams. GET /orgs/{orgId}/teams on the accounts host answers 403 forbidden, so keep the team IDs you need in your configuration. A project’s teams give you their IDs.
  • delete a ticket. Archive it instead, with POST /tickets/{ticketId}/archive.
  • create labels. That needs an organization admin, signed in.
  • attach a team to a project or detach one. That’s a project admin’s job, signed in.
  • move a ticket into the column that starts an agent run (the column with the building role). Starting an agent should be a person’s call.
  • see restricted projects. They don’t appear in its lists, and their tickets are refused.
  • merge or delete a customer. Both change other people’s data at once, so they’re an organization admin’s, signed in.
  • change Customers settings or turn Customers on, including statuses, tiers, the revenue unit and who sees revenue. Those are an organization admin’s, signed in (see Customers settings and privacy).
  • forget a requester, or export a customer’s requests as CSV. Both are about personal data, so a person does them in the app.

Anything a key creates, a ticket or a comment, shows the key as its author, not a person.

Revoke a key in Settings › Organization › Security. It stops working at once: it can’t be exchanged any more, and tokens already issued from it stop working within seconds. An expired key behaves the same way.

To rotate a key without downtime, create the new key, switch your integration over, and only then revoke the old one.