Version 1.0.0
Droomwork IAM
Registration, sessions, API keys and the token grant.
https://sandbox.droomwork.ioDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…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?
/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
requiredThe 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
requiredThe person registering.
email
string · email
requiredThe 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
requiredLong 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"
requiredAlways registration. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
required2 fields of OrganisationDetail
id
string
requiredYour 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
requiredThe organisation's name, as it was given when the organisation was registered.
account
AccountDetail
required2 fields of AccountDetail
email
string · email
requiredThe address this account signs in with. It's the email you send to POST /v1/sessions.
name
string
requiredThe name of the person this account belongs to, as it was given when the account was created.
session
object
requiredYou'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
requiredSend 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
requiredWhen this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again at POST /v1/sessions after that.
api_key
object
required10 fields
id
string
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
key
string
requiredShown here and never again. Send it as the Droomwork-Api-Key header.
next
string
requiredWhat to do with the key you were just given.
Other responses
Errors it can return
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)
{
"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"
}
/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
requiredThe address the account signs in with. An address that was never registered is refused the same way as a wrong password.
password
string
requiredThe 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"
requiredAlways session. Tells you which kind of record you are looking at, so one handler can read any response.
token
string
requiredSend 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
requiredWhen this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again after that.
account
AccountDetail
required2 fields of AccountDetail
email
string · email
requiredThe address this account signs in with. It's the email you send to POST /v1/sessions.
name
string
requiredThe name of the person this account belongs to, as it was given when the account was created.
organisation
OrganisationDetail
required2 fields of OrganisationDetail
id
string
requiredYour 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
requiredThe organisation's name, as it was given when the organisation was registered.
Other responses
Errors it can return
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)
{
"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"
}
}
/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"
requiredAlways client_credentials, the one grant this endpoint supports. Anything else, or leaving it out, is refused with invalid_request.
client_id
string
requiredThe 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
requiredThe 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
optionalA subset of what the client holds. Omitted means all of them.
Returns
The token.
object
always "token"
requiredAlways token. Tells you which kind of record you are looking at, so one handler can read any response.
access_token
string
requiredThe 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"
requiredAlways Bearer. Send access_token in the Authorization header with that prefix.
expires_in
integer
requiredSeconds. Fifteen minutes.
scopes
array of Scope
requiredWhat 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
Errors it can return
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)
{
"object": "token",
"access_token": "example",
"token_type": "Bearer",
"expires_in": 1,
"scopes": [
"run:read"
]
}
/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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of ApiKey
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
9 fields of ApiKey
id
string
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
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)
{
"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
}
/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
requiredA 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
optionalWhat this key is for, so a list of them can be read later.
scopes
array of Scope
optionalThe 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
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
key
string
requiredShown here and never again. Send it as the Droomwork-Api-Key header.
Other responses
Errors it can return
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)
{
"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"
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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)
{
"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"
}
/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
optionalHow 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of SupportAccess
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
5 fields of SupportAccess
id
string
requiredThis 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"
requiredAlways support_access. Tells you which kind of record you are looking at, so one handler can read any response.
at
string · date-time
requiredWhen 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
requiredWhat 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
optionalNull for a look recorded before purposes were kept.
SupportAccessPurposeorhas_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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)
{
"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
}
/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"
requiredAlways account. Tells you which kind of record you are looking at, so one handler can read any response.
account
AccountDetail
required2 fields of AccountDetail
email
string · email
requiredThe address this account signs in with. It's the email you send to POST /v1/sessions.
name
string
requiredThe name of the person this account belongs to, as it was given when the account was created.
organisation
OrganisationDetail
required2 fields of OrganisationDetail
id
string
requiredYour 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
requiredThe organisation's name, as it was given when the organisation was registered.
api_keys
array of ApiKey
requiredEvery 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
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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)
{
"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"
}
]
}
/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
requiredThe address of the account whose password you're replacing: the one the starting password was sent for.
current_password
string
requiredThe password Droomwork sent you.
new_password
string
requiredOne only you know. Twelve characters at least.
Returns
The password is yours, and this is the session for it.
object
always "session"
requiredAlways session. Tells you which kind of record you are looking at, so one handler can read any response.
token
string
requiredSend 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
requiredWhen this session stops being accepted, as an RFC 3339 timestamp in UTC. Sign in again after that.
account
AccountDetail
required2 fields of AccountDetail
email
string · email
requiredThe address this account signs in with. It's the email you send to POST /v1/sessions.
name
string
requiredThe name of the person this account belongs to, as it was given when the account was created.
organisation
OrganisationDetail
required2 fields of OrganisationDetail
id
string
requiredYour 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
requiredThe organisation's name, as it was given when the organisation was registered.
Other responses
Errors it can return
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)
{
"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"
}
}
/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
optionalWhat this key is for, so a list of them can be read later.
scopes
array of Scope
optionalThe 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
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
key
string
requiredShown here and never again. Send it as the Droomwork-Api-Key header.
Other responses
Errors it can return
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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways api_key. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredFalse 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
requiredTrue 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
requiredWhat this key is for, as named when it was created. Use it to tell your keys apart in a list.
realm
string
requiredWhich 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.
testlivescopes
array of Scope
requiredWhat 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
optionalWhen 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
requiredWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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)
{
"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"
}