DDroomwork Developers

Version 1.0.0

Droomwork IAM

Registration, sessions, API keys and the token grant.

Base URLhttps://sandbox.droomwork.io
AuthenticationDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…
Events it sends2 webhooks →
Signed in: requests you run on this page use a key made for your account. Your keys →

Every request you send carries a credential. This is where you get one.

What you should know before you start

You have two kinds of credential, for different jobs. An API key is long lived; send it from your server on every request. An access token is minted from a client identifier and secret, lasts fifteen minutes, and carries only the scopes you asked for. Start with a key. Use tokens when you want to hand a narrower credential to part of your system; the client identifier and secret behind them are issued by Droomwork on request, and there is no refresh token, you mint again.

You never name your organisation. It comes from your credential, on every endpoint. No request takes an organisation, and you can't act for one you don't hold a credential for.

Scopes are granular and we check them. run:read can't approve a payroll run: looking at money and moving it are different scopes. There are three actions for each of the eight modules, plus ledger:read, and delivery:read and delivery:write for your webhook endpoints. Ask for what you need and no more.

You see a secret once. An API key is returned when you create it and never again. No endpoint reads one back.

Sandbox keys are real keys. They authenticate for real against real records. What makes one a sandbox key is the realm it reaches, and livemode tells you which.

Getting started

Register. One call gives you an organisation, an account, a session and a first sandbox key. Send the key as Droomwork-Api-Key on any sandbox request. When you want short-lived credentials, ask Droomwork for a client identifier and secret and exchange them at the token grant.

Every operation below has two samples: Direct HTTP, with nothing but your language's own client, and Client library, with ours. Same request, same answer; the switch above each sample picks one and remembers it. Which should I use?

POST/v1/registrations#

Register an organisation

iam.registrations.create

One call gives you an organisation, an account to sign in with, a session, and a first sandbox API key.

You send no credential here; it's where your credentials begin. You come out signed in, so there's no need to sign in again with what you just typed.

The key in this response is shown once.

Body

organisation_name string required

The name of the organisation you're registering. It's what organisation.name shows wherever the organisation appears, so give the name you trade under.

name string required

The person registering.

email string · email required

The address the person registering will sign in with. One address belongs to one person: an address that already has an account is refused with conflict.

password string required

Long rather than complicated. Refused with a reason you can show somebody, never a rule they have to guess.

Returns

The organisation, the account, a session and the first key.

object always "registration" required

Always registration. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

organisation OrganisationDetail required
2 fields of OrganisationDetail
id string required

Your organisation's identifier; it starts with org_ and never changes. It reads the same wherever organisation appears, from POST /v1/registrations to GET /v1/me, and you never send it: your credential names your organisation on every request.

name string required

The organisation's name, as it was given when the organisation was registered.

account AccountDetail required
2 fields of AccountDetail
email string · email required

The address this account signs in with. It's the email you send to POST /v1/sessions.

name string required

The name of the person this account belongs to, as it was given when the account was created.

session object required

You're signed in from this moment: a token to send as Droomwork-Session, and when it stops being accepted. No need to sign in again with what you just typed.

2 fields
token string required

Send it as the Droomwork-Session header on the endpoints a signed-in person uses, such as GET /v1/me. It acts as this account, so keep it to yourself.

expires_at string · date-time required

When this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again at POST /v1/sessions after that.

api_key object required
10 fields
id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

key string required

Shown here and never again. Send it as the Droomwork-Api-Key header.

next string required

What to do with the key you were just given.

Other responses

400The request could not be read, or a value was refused.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/registrations" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"organisation_name":"Adaeze Foods Ltd","name":"Ada Obi","email":"ada@adaezefoods.example","password":"correct horse battery staple"}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamRegistrationsCreate({
  iamRegistrationRequest: {"organisationName":"Adaeze Foods Ltd","name":"Ada Obi","email":"ada@adaezefoods.example","password":"correct horse battery staple"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/registrations', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "organisation_name": "Adaeze Foods Ltd",
    "name": "Ada Obi",
    "email": "ada@adaezefoods.example",
    "password": "correct horse battery staple"
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_registrations_create(body={"organisation_name": "Adaeze Foods Ltd", "name": "Ada Obi", "email": "ada@adaezefoods.example", "password": "correct horse battery staple"})
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/registrations',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"organisation_name": "Adaeze Foods Ltd", "name": "Ada Obi", "email": "ada@adaezefoods.example", "password": "correct horse battery staple"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamRegistrationsCreate(json_decode('{"organisation_name":"Adaeze Foods Ltd","name":"Ada Obi","email":"ada@adaezefoods.example","password":"correct horse battery staple"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/registrations');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"organisation_name":"Adaeze Foods Ltd","name":"Ada Obi","email":"ada@adaezefoods.example","password":"correct horse battery staple"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamRegistrationsCreate(body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/registrations"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "organisation_name": "Adaeze Foods Ltd",
          "name": "Ada Obi",
          "email": "ada@adaezefoods.example",
          "password": "correct horse battery staple"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamRegistrationsCreate(body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/registrations");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "organisation_name": "Adaeze Foods Ltd",
      "name": "Ada Obi",
      "email": "ada@adaezefoods.example",
      "password": "correct horse battery staple"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamRegistrationsCreate(ctx).IamRegistrationRequest(body).Execute()
body := strings.NewReader(`{
  "organisation_name": "Adaeze Foods Ltd",
  "name": "Ada Obi",
  "email": "ada@adaezefoods.example",
  "password": "correct horse battery staple"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/registrations", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "registration",
  "livemode": true,
  "mocked": false,
  "organisation": {
    "id": "org_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "name": "Rivers State Internal Revenue Service"
  },
  "account": {
    "email": "ada@adaezefoods.example",
    "name": "Rivers State Internal Revenue Service"
  },
  "session": {
    "token": "example",
    "expires_at": "2026-09-01T09:00:00Z"
  },
  "api_key": {
    "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "object": "api_key",
    "livemode": true,
    "mocked": false,
    "name": "Rivers State Internal Revenue Service",
    "realm": "test",
    "scopes": [
      "run:read"
    ],
    "created_at": "2026-09-01T09:00:00Z",
    "revoked_at": "2026-09-01T09:00:00Z",
    "key": "dw_test_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  },
  "next": "example"
}
POST/v1/sessions#

Sign in

iam.sessions.create

Send an email and password and get a session token back.

A wrong password and an address that was never registered are refused identically, so the response can't tell you which addresses have accounts.

If we created the account on your behalf and it still holds the password it was sent, you get password_reset_required instead of the wrong-password answer. Send that person to a reset screen rather than telling them their password is wrong. Replace the password at POST /v1/account/password, which returns the session. Only somebody who already holds the password reaches either answer, so telling them apart gives nothing away.

Body

email string · email required

The address the account signs in with. An address that was never registered is refused the same way as a wrong password.

password string required

The account's own password. If it's still the one Droomwork sent, you get password_reset_required: replace it at POST /v1/account/password instead.

Returns

The session.

object always "session" required

Always session. Tells you which kind of record you are looking at, so one handler can read any response.

token string required

Send it as the Droomwork-Session header on the endpoints a signed-in person uses, such as GET /v1/me. It isn't an API key, and nothing outside those endpoints accepts it.

expires_at string · date-time required

When this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again after that.

account AccountDetail required
2 fields of AccountDetail
email string · email required

The address this account signs in with. It's the email you send to POST /v1/sessions.

name string required

The name of the person this account belongs to, as it was given when the account was created.

organisation OrganisationDetail required
2 fields of OrganisationDetail
id string required

Your organisation's identifier; it starts with org_ and never changes. It reads the same wherever organisation appears, from POST /v1/registrations to GET /v1/me, and you never send it: your credential names your organisation on every request.

name string required

The organisation's name, as it was given when the organisation was registered.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/sessions" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@adaezefoods.example","password":"correct horse battery staple"}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamSessionsCreate({
  iamSignInRequest: {"email":"ada@adaezefoods.example","password":"correct horse battery staple"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/sessions', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "email": "ada@adaezefoods.example",
    "password": "correct horse battery staple"
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_sessions_create(body={"email": "ada@adaezefoods.example", "password": "correct horse battery staple"})
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/sessions',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"email": "ada@adaezefoods.example", "password": "correct horse battery staple"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamSessionsCreate(json_decode('{"email":"ada@adaezefoods.example","password":"correct horse battery staple"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sessions');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"email":"ada@adaezefoods.example","password":"correct horse battery staple"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamSessionsCreate(body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sessions"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "email": "ada@adaezefoods.example",
          "password": "correct horse battery staple"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamSessionsCreate(body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sessions");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "email": "ada@adaezefoods.example",
      "password": "correct horse battery staple"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamSessionsCreate(ctx).IamSignInRequest(body).Execute()
body := strings.NewReader(`{
  "email": "ada@adaezefoods.example",
  "password": "correct horse battery staple"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sessions", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "session",
  "token": "example",
  "expires_at": "2026-09-01T09:00:00Z",
  "account": {
    "email": "ada@adaezefoods.example",
    "name": "Rivers State Internal Revenue Service"
  },
  "organisation": {
    "id": "org_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "name": "Rivers State Internal Revenue Service"
  }
}
POST/v1/oauth/token#

Mint an access token

iam.oauth.token

Send a client identifier and secret and get a short-lived bearer token back.

A client identifier and secret are issued by Droomwork on request; no endpoint creates or lists them, and the sandbox holds none until one is issued to you. Until then, use your API key. There is no refresh token: when expires_in runs out, send the same request again.

Ask for a subset of the scopes the client holds and you get that subset. Ask for anything beyond them and the whole request is refused; it's never quietly narrowed to what you were allowed.

An unknown client and a wrong secret are refused identically, so the response can't tell you which clients exist.

Body

grant_type always "client_credentials" required

Always client_credentials, the one grant this endpoint supports. Anything else, or leaving it out, is refused with invalid_request.

client_id string required

The identifier of a client Droomwork issued you on request, sent with its client_secret; no endpoint creates or lists clients, and the sandbox holds none until one is issued. An unknown identifier and a wrong secret are refused identically, so the response can't tell you which clients exist.

client_secret string required

The secret issued with client_id. Keep it on your server; a wrong secret is refused the same way as an unknown client.

scopes array of Scope optional

A subset of what the client holds. Omitted means all of them.

Returns

The token.

object always "token" required

Always token. Tells you which kind of record you are looking at, so one handler can read any response.

access_token string required

The bearer token. Send it as Authorization: Bearer <token> on every request until expires_in runs out, then mint another with the same client identifier and secret; there is no refresh token.

token_type always "Bearer" required

Always Bearer. Send access_token in the Authorization header with that prefix.

expires_in integer required

Seconds. Fifteen minutes.

scopes array of Scope required

What this token may do, as module:action scopes. Exactly what you asked for, or everything the client holds if you asked for none.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/oauth/token" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"client_credentials","client_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","client_secret":"example","scopes":["run:read"]}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamOauthToken({
  iamTokenRequest: {"grantType":"client_credentials","clientId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","clientSecret":"example","scopes":["run:read"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/oauth/token', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "grant_type": "client_credentials",
    "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "client_secret": "example",
    "scopes": [
      "run:read"
    ]
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_oauth_token(body={"grant_type": "client_credentials", "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "client_secret": "example", "scopes": ["run:read"]})
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/oauth/token',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"grant_type": "client_credentials", "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "client_secret": "example", "scopes": ["run:read"]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamOauthToken(json_decode('{"grant_type":"client_credentials","client_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","client_secret":"example","scopes":["run:read"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/oauth/token');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"grant_type":"client_credentials","client_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","client_secret":"example","scopes":["run:read"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamOauthToken(body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/oauth/token"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "grant_type": "client_credentials",
          "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "client_secret": "example",
          "scopes": [
            "run:read"
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamOauthToken(body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/oauth/token");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "grant_type": "client_credentials",
      "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "client_secret": "example",
      "scopes": [
        "run:read"
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamOauthToken(ctx).IamTokenRequest(body).Execute()
body := strings.NewReader(`{
  "grant_type": "client_credentials",
  "client_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "client_secret": "example",
  "scopes": [
    "run:read"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/oauth/token", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "token",
  "access_token": "example",
  "token_type": "Bearer",
  "expires_in": 1,
  "scopes": [
    "run:read"
  ]
}
GET/v1/api_keys#

List API keys

iam.api_keys.list

The keys your organisation holds, without their secrets. No endpoint returns a secret again.

Returns

A page of keys.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of ApiKey required

The records on this page, in the order the list promises. Empty when nothing matched.

9 fields of ApiKey
id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/api_keys" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamApiKeysList({});
const response = await fetch('https://sandbox.droomwork.io/v1/api_keys', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_api_keys_list()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/api_keys',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamApiKeysList();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/api_keys');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamApiKeysList();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/api_keys"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamApiKeysList();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/api_keys");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamApiKeysList(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/api_keys", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "api_key",
      "livemode": true,
      "mocked": false,
      "name": "Rivers State Internal Revenue Service",
      "realm": "test",
      "scopes": [
        "run:read"
      ],
      "created_at": "2026-09-01T09:00:00Z",
      "revoked_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
POST/v1/api_keys#

Create an API key

iam.api_keys.create

Mints another key for the organisation behind the credential you're using.

You can't name the organisation in the request; it comes from your credential. A body carrying one is refused, not ignored.

The key is shown once. You can't issue the wildcard scope.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body optional

name string optional

What this key is for, so a list of them can be read later.

scopes array of Scope optional

The scopes the key should carry, each module:action; name only what it needs. A value that isn't a scope refuses the whole request, and you can't issue the wildcard.

Returns

The key, shown once.

id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

key string required

Shown here and never again. Send it as the Droomwork-Api-Key header.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/api_keys" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI","scopes":["run:read"]}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamApiKeysCreate({
  idempotencyKey: crypto.randomUUID(),
  iamApiKeyCreateRequest: {"name":"CI","scopes":["run:read"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/api_keys', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "name": "CI",
    "scopes": [
      "run:read"
    ]
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_api_keys_create(body={"name": "CI", "scopes": ["run:read"]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/api_keys',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"name": "CI", "scopes": ["run:read"]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamApiKeysCreate($idempotencyKey, json_decode('{"name":"CI","scopes":["run:read"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/api_keys');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"name":"CI","scopes":["run:read"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamApiKeysCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/api_keys"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "name": "CI",
          "scopes": [
            "run:read"
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamApiKeysCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/api_keys");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "name": "CI",
      "scopes": [
        "run:read"
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamApiKeysCreate(ctx).IdempotencyKey(key).IamApiKeyCreateRequest(body).Execute()
body := strings.NewReader(`{
  "name": "CI",
  "scopes": [
    "run:read"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/api_keys", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "api_key",
  "livemode": true,
  "mocked": false,
  "name": "Rivers State Internal Revenue Service",
  "realm": "test",
  "scopes": [
    "run:read"
  ],
  "created_at": "2026-09-01T09:00:00Z",
  "revoked_at": "2026-09-01T09:00:00Z",
  "key": "dw_test_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
POST/v1/api_keys/{id}/revoke#

Revoke an API key

iam.api_keys.revoke

Stops a key authenticating from the moment this returns. Revoking isn't deleting: the key stays listed with revoked_at set, so its history stays with it.

Path parameters

id string required

The key's id, which starts with api_key_, as returned by POST /v1/api_keys or POST /v1/account/api_keys when you made it and listed by GET /v1/api_keys and GET /v1/me. A key belonging to another organisation answers not_found.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The revoked key.

id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamApiKeysRevoke({ id: 'api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_api_keys_revoke(id='api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamApiKeysRevoke(id: 'api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamApiKeysRevoke("api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamApiKeysRevoke(id: "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamApiKeysRevoke(ctx, "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "api_key",
  "livemode": true,
  "mocked": false,
  "name": "Rivers State Internal Revenue Service",
  "realm": "test",
  "scopes": [
    "run:read"
  ],
  "created_at": "2026-09-01T09:00:00Z",
  "revoked_at": "2026-09-01T09:00:00Z"
}
GET/v1/support_access#

When Droomwork looked at your data

iam.support_access.list

Every time a Droomwork staff member read your organisation's data, newest first, with what it was for.

We can only reach your organisation when access has been deliberately granted, and every look is recorded before it's answered. This is that record, from your side.

You'll also see the same reads in your own audit trail, marked actor_type: staff. That tells you a read happened. This tells you why.

It doesn't tell you who. The purpose comes from a fixed list, never free text. If you need to take something further, quote the id and we can identify the person internally.

Query parameters

limit integer optional

How many records to return, from 1 to 200, newest first. Leave it out to get 50.

Returns

When Droomwork looked, and what for.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of SupportAccess required

The records on this page, in the order the list promises. Empty when nothing matched.

5 fields of SupportAccess
id string required

This look's identifier; it starts with sta_ and is returned only by GET /v1/support_access. The record never names who looked, so if you need to take something further, quote this id to us and we can identify the person internally.

object always "support_access" required

Always support_access. Tells you which kind of record you are looking at, so one handler can read any response.

at string · date-time required

When the read happened, as an RFC 3339 timestamp in UTC. Line it up with the entry marked actor_type: staff in your own audit trail.

action string required

What was read, as the method and the path. The same shape your audit trail uses, so you can line the two up.

purpose one of optional

Null for a look recorded before purposes were kept.

SupportAccessPurposeor
has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional)
curl -X GET "https://sandbox.droomwork.io/v1/support_access?limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional)
const result = await api.iamSupportAccessList({ limit: 25 });
// query parameters: limit (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/support_access?limit=25', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

# query parameters: limit (optional)
result = api.iam_support_access_list(limit=25)
import os

import requests

# query parameters: limit (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/support_access?limit=25',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional)
$result = $api->iamSupportAccessList(limit: 25);
<?php
// query parameters: limit (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/support_access?limit=25');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

// query parameters: limit (optional)
var result = api.iamSupportAccessList(25);
// query parameters: limit (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/support_access?limit=25"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

// query parameters: limit (optional)
var result = api.IamSupportAccessList(limit: 25);
// query parameters: limit (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/support_access?limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

// query parameters: limit (optional)
result, _, err := client.IAMAPI.IamSupportAccessList(ctx).Limit(25).Execute()
// query parameters: limit (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/support_access?limit=25", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "sta_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "support_access",
      "at": "2026-09-01T09:00:00Z",
      "action": "GET /v1/payroll/runs",
      "purpose": "support_ticket"
    }
  ],
  "has_more": true
}
GET/v1/me#

Who this credential belongs to

iam.me.retrieve

The account and organisation behind the session you're holding, with the organisation's keys.

Send a session token, not an API key. This is what the developer portal calls after you sign in.

Returns

The account, the organisation and its keys.

object always "account" required

Always account. Tells you which kind of record you are looking at, so one handler can read any response.

account AccountDetail required
2 fields of AccountDetail
email string · email required

The address this account signs in with. It's the email you send to POST /v1/sessions.

name string required

The name of the person this account belongs to, as it was given when the account was created.

organisation OrganisationDetail required
2 fields of OrganisationDetail
id string required

Your organisation's identifier; it starts with org_ and never changes. It reads the same wherever organisation appears, from POST /v1/registrations to GET /v1/me, and you never send it: your credential names your organisation on every request.

name string required

The organisation's name, as it was given when the organisation was registered.

api_keys array of ApiKey required

Every API key the organisation holds, without their secrets; revoked keys stay listed with revoked_at set. No endpoint returns a secret again.

9 fields of ApiKey
id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/me" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamMeRetrieve({});
const response = await fetch('https://sandbox.droomwork.io/v1/me', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_me_retrieve()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/me',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamMeRetrieve();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/me');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamMeRetrieve();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/me"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamMeRetrieve();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/me");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamMeRetrieve(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/me", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "account",
  "account": {
    "email": "ada@adaezefoods.example",
    "name": "Rivers State Internal Revenue Service"
  },
  "organisation": {
    "id": "org_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "name": "Rivers State Internal Revenue Service"
  },
  "api_keys": [
    {
      "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "api_key",
      "livemode": true,
      "mocked": false,
      "name": "Rivers State Internal Revenue Service",
      "realm": "test",
      "scopes": [
        "run:read"
      ],
      "created_at": "2026-09-01T09:00:00Z",
      "revoked_at": "2026-09-01T09:00:00Z"
    }
  ]
}
POST/v1/account/password#

Replace the password you were sent

iam.account.password.replace

Set a password of your own, using the one you were sent as the current one, and get signed in.

When we create an account on your behalf, we send a password to start with. You can't sign in with it: it's a one-time credential for this endpoint, it expires, and replacing it is what makes it stop working. Try to sign in with it and POST /v1/sessions refuses with password_reset_required and points here.

Send no credential; you don't have one yet. The change ends every session the account holds.

An expired invitation is refused here as well as at sign-in. Ask us for another.

Body

email string · email required

The address of the account whose password you're replacing: the one the starting password was sent for.

current_password string required

The password Droomwork sent you.

new_password string required

One only you know. Twelve characters at least.

Returns

The password is yours, and this is the session for it.

object always "session" required

Always session. Tells you which kind of record you are looking at, so one handler can read any response.

token string required

Send it as the Droomwork-Session header on the endpoints a signed-in person uses, such as GET /v1/me. It isn't an API key, and nothing outside those endpoints accepts it.

expires_at string · date-time required

When this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again after that.

account AccountDetail required
2 fields of AccountDetail
email string · email required

The address this account signs in with. It's the email you send to POST /v1/sessions.

name string required

The name of the person this account belongs to, as it was given when the account was created.

organisation OrganisationDetail required
2 fields of OrganisationDetail
id string required

Your organisation's identifier; it starts with org_ and never changes. It reads the same wherever organisation appears, from POST /v1/registrations to GET /v1/me, and you never send it: your credential names your organisation on every request.

name string required

The organisation's name, as it was given when the organisation was registered.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/account/password" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@adaezefoods.example","current_password":"example","new_password":"example correct"}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamAccountPasswordReplace({
  iamAccountPasswordRequest: {"email":"ada@adaezefoods.example","currentPassword":"example","newPassword":"example correct"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/account/password', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "email": "ada@adaezefoods.example",
    "current_password": "example",
    "new_password": "example correct"
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_account_password_replace(body={"email": "ada@adaezefoods.example", "current_password": "example", "new_password": "example correct"})
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/account/password',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"email": "ada@adaezefoods.example", "current_password": "example", "new_password": "example correct"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamAccountPasswordReplace(json_decode('{"email":"ada@adaezefoods.example","current_password":"example","new_password":"example correct"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/account/password');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"email":"ada@adaezefoods.example","current_password":"example","new_password":"example correct"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamAccountPasswordReplace(body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/account/password"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "email": "ada@adaezefoods.example",
          "current_password": "example",
          "new_password": "example correct"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamAccountPasswordReplace(body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/account/password");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "email": "ada@adaezefoods.example",
      "current_password": "example",
      "new_password": "example correct"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamAccountPasswordReplace(ctx).IamAccountPasswordRequest(body).Execute()
body := strings.NewReader(`{
  "email": "ada@adaezefoods.example",
  "current_password": "example",
  "new_password": "example correct"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/account/password", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "session",
  "token": "example",
  "expires_at": "2026-09-01T09:00:00Z",
  "account": {
    "email": "ada@adaezefoods.example",
    "name": "Rivers State Internal Revenue Service"
  },
  "organisation": {
    "id": "org_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "name": "Rivers State Internal Revenue Service"
  }
}
POST/v1/account/api_keys#

Create an API key while signed in

iam.account.api_keys.create

Mints a key for the organisation behind the session you're holding.

The same key POST /v1/api_keys issues, reached with a session instead of a key. It's how you get your first one after signing in, or after being invited by us with only a password.

You can't name the organisation in the request; it comes from your session. The key is shown once. You can't issue the wildcard scope.

Takes no Idempotency-Key, unlike POST /v1/api_keys. Repeat the call and you mint a second key rather than getting the first back.

Body optional

name string optional

What this key is for, so a list of them can be read later.

scopes array of Scope optional

The scopes the key should carry, each module:action; name only what it needs. A value that isn't a scope refuses the whole request, and you can't issue the wildcard.

Returns

The key, shown once.

id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

key string required

Shown here and never again. Send it as the Droomwork-Api-Key header.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/account/api_keys" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI","scopes":["run:read"]}'
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamAccountApiKeysCreate({
  iamApiKeyCreateRequest: {"name":"CI","scopes":["run:read"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/account/api_keys', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "CI",
    "scopes": [
      "run:read"
    ]
  }),
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_account_api_keys_create(body={"name": "CI", "scopes": ["run:read"]})
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/account/api_keys',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"name": "CI", "scopes": ["run:read"]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamAccountApiKeysCreate(json_decode('{"name":"CI","scopes":["run:read"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/account/api_keys');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"name":"CI","scopes":["run:read"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamAccountApiKeysCreate(body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/account/api_keys"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "name": "CI",
          "scopes": [
            "run:read"
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamAccountApiKeysCreate(body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/account/api_keys");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "name": "CI",
      "scopes": [
        "run:read"
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamAccountApiKeysCreate(ctx).IamApiKeyCreateRequest(body).Execute()
body := strings.NewReader(`{
  "name": "CI",
  "scopes": [
    "run:read"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/account/api_keys", body)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "api_key",
  "livemode": true,
  "mocked": false,
  "name": "Rivers State Internal Revenue Service",
  "realm": "test",
  "scopes": [
    "run:read"
  ],
  "created_at": "2026-09-01T09:00:00Z",
  "revoked_at": "2026-09-01T09:00:00Z",
  "key": "dw_test_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
POST/v1/account/api_keys/{id}/revoke#

Revoke an API key while signed in

iam.account.api_keys.revoke

Stops a key working, for the organisation behind the session you're holding.

The same act as POST /v1/api_keys/{id}/revoke, reached with a session. A key belonging to another organisation answers not_found; this endpoint never tells you whether somebody else's key exists.

Path parameters

id string required

The key's id, which starts with api_key_, as returned by POST /v1/api_keys or POST /v1/account/api_keys when you made it and listed by GET /v1/api_keys and GET /v1/me. A key belonging to another organisation answers not_found.

Returns

The key, with the moment it stopped working.

id string required

The key's identifier; it starts with api_key_ and never changes. Returned by POST /v1/api_keys, POST /v1/account/api_keys and POST /v1/registrations, listed by GET /v1/api_keys and GET /v1/me, and sent as {id} to revoke it.

object always "api_key" required

Always api_key. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

False for a record in the sandbox realm, true for one in the live realm. This answers which data a record belongs to. mocked answers where its numbers came from, and the two are separate questions. A sandbox payslip computed by the real engine is livemode false and mocked false. A deferred capability in the live realm is livemode true and mocked true. See DEC-068 and ADR-0011.

mocked boolean required

True when this response came from a mock rather than from a calculation, and false when the engine computed it. Required by M1 row 10. It stays true for a capability that is deferred by section 3 of the contract, even in the live realm, because that capability is still on its documented mock. It is deliberately not called sandbox: sandbox is the name of a realm, and livemode is the field that says which realm you are in. See ADR-0011.

name string required

What this key is for, as named when it was created. Use it to tell your keys apart in a list.

realm string required

Which realm this key reaches: test is the sandbox, where nothing moves, and live is real money and real filings. Sandbox keys are test and carry livemode false; live keys aren't self-serve.

testlive
scopes array of Scope required

What this key may do, as module:action scopes; a request needing one not listed here is refused. You can't issue the wildcard, so it never appears here.

revoked_at string · date-time · nullable optional

When this key stopped authenticating, as an RFC 3339 timestamp in UTC, or null while it still works. A revoked key stays listed with this set, so its history stays with it.

created_at string · date-time required

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, IAMApi } from '@droomwork/sdk';

const api = new IAMApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.iamAccountApiKeysRevoke({ id: 'api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import droomwork

config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.IAMApi(client)

result = api.iam_account_api_keys_revoke(id='api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = DroomworkSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\Api\IAMApi(new GuzzleHttp\Client(), $config);

$result = $api->iamAccountApiKeysRevoke(id: 'api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.IamApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
IamApi api = new IamApi(client);

var result = api.iamAccountApiKeysRevoke("api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Droomwork.Sdk.Api;
using Droomwork.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new IAMApi(config);

var result = api.IamAccountApiKeysRevoke(id: "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import droomwork "github.com/fenibofubara/droomwork-sdk-go"

ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)

result, _, err := client.IAMAPI.IamAccountApiKeysRevoke(ctx, "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/account/api_keys/api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "api_key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "api_key",
  "livemode": true,
  "mocked": false,
  "name": "Rivers State Internal Revenue Service",
  "realm": "test",
  "scopes": [
    "run:read"
  ],
  "created_at": "2026-09-01T09:00:00Z",
  "revoked_at": "2026-09-01T09:00:00Z"
}