DDroomwork Developers

Version 1.0.0

Droomwork MATCH GEO-AWARE WORKFORCE ORCHESTRATOR

Allocation of work to available people, and proof that the work happened.

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

MATCH gets your work to the people who can do it: open a work order, get a ranked cascade of offers, and receive sealed proof that the work happened.

What you should know before you start

Only eligible workers are ranked. A worker needs a live unrevoked Passport, an executed engagement, every hard constraint met and a current availability state before they appear in a ranking at all. A ranking you read is a list of people who could lawfully do the work. You never have to filter it.

Availability is set by the worker and never inferred. Movement, silence and heartbeats never change it. It changes only when the worker changes it, through the one endpoint they own.

Location is collected only while a worker is available or on assignment. Send a reading outside those windows and it's refused, not stored. The supply state shows the window as closed with no position on it, so you can prove the absence from the data.

No single signal decides anything that matters. Spoofing needs several independent signals, and one on its own suspends nobody. Arrival needs geofence entry corroborated by a start passcode. Completion needs a completion passcode, dwell duration and telemetry continuity. Where the signals disagree the assignment goes to exception for a person to adjudicate, and does not settle.

Every ranking is kept in full. Every candidate considered, every component score, the weight set version and the outcome. You can reproduce a ranking months later, and a worker who was not offered the work is told why in plain language from a fixed set of reasons.

Travel time is measured over the road network. Not straight line distance. A river between two points is a river, not a short hop.

A fairness floor is reported continuously. Eligible workers in the long tail get a defined minimum offer exposure, and you can read the dispersion of offers across the eligible population at any time, not only at the end of a period.

An unfilled order tells you why. Cascade exhaustion and expiry are declared with a specific reason and emitted as a supply gap, so a failure to fill becomes sourcing demand rather than silence.

MATCH computes no money and moves none. The proof of service bundle carries evidence and never an amount. Droomwork Pay settles against the sealed bundle.

Removal from a live cascade takes 60 seconds at most. A revocation from ANCHOR or a lapse of legal standing from RAIL removes the worker from every live cascade and cancels any unstarted assignment, within 60 seconds.

Getting started

Open a work order. Read the ranking it produced. Watch the offers cascade. Accept an offer to turn it into an assignment. Confirm arrival. Confirm completion. Read the sealed proof.

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/allocation/heartbeats#

Report a batch of worker heartbeats

allocation.heartbeats.create

Send a worker's position, connectivity quality and device condition, and get back the operational zone they resolved to.

Send a batch, not a single reading. A device that loses connectivity can buffer what it could not send and deliver it on reconnection.

Give each reading the client clock time it was taken at. Readings are reconciled by that time rather than by arrival order, so a late batch never creates movement that never happened or presence at a moment the worker was not there. Readings that would imply either are refused and named in the report.

A reading taken while the worker was neither available nor on assignment is refused with outside_collection_window and no position is stored.

The availability field on a reading is what the device believed at the time. It is read for drift detection and never changes the availability state, which changes only through the endpoint the worker owns.

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

worker_id string required

The worker every reading is for: the sub_ subject reference you chose and sent as subject_ref at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR. One batch carries one worker.

readings array of Heartbeat required

Ordered by the client where possible. The server reconciles regardless.

7 fields of Heartbeat
recorded_at string · date-time required

The client clock time the reading was taken at. Reconciliation orders by this rather than by arrival, so a buffered batch does not create movement that never happened.

position one of optional

Omitted where the collection window was closed. Sending one anyway is refused rather than stored.

GeoPointor
connectivity string required

The quality of the link at the time of the reading: offline for no link at all, then poor, fair or good for a working link from weakest to strongest.

offlinepoorfairgood
availability one of optional

What the device believed the worker's availability was. Read for drift detection and never written to the supply state, which changes only through the endpoint the worker owns.

AvailabilityStateor
battery_percent integer · nullable · minimum 0 · maximum 100 optional

Device condition. A dying battery explains a gap that would otherwise read as absence.

mock_location_detected boolean · nullable optional

One integrity signal of four. On its own it suspends nobody.

device_integrity_attested boolean · nullable optional

Whether the platform integrity attestation passed on the device.

Returns

The batch was accepted and reconciled. Readings that were refused are named individually, and the rest are stored.

object always "heartbeat_receipt" required

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

worker_id string required

The worker the batch was for: the sub_ subject reference you sent as worker_id in the request to POST /v1/allocation/heartbeats, returned unchanged.

accepted_count integer · minimum 0 required

How many of the readings you sent were accepted, from 0 up. A reading is either counted here or named in rejections.

rejected_count integer · minimum 0 required

How many of the readings you sent were refused, from 0 up. Each one is named in rejections with its index and reason, so a refused position is never a silent gap.

reconciled boolean required

True when readings arrived out of order or late and were reordered by their recorded time.

zone one of optional

The zone the worker resolved to after reconciliation.

Zoneor
presence string optional

What was last heard from the worker, which is separate from what they set. A worker can be available and absent at the same time. That combination is ghost supply.

reportingstaleghost
validation ValidationReport required

The result of checking a submission before anything is committed. Names every failing entry rather than stopping at the first, so one submission tells you everything to fix. The same shape wherever the platform ingests, validates and reports, which is every bulk intake in every module.

4 fields of ValidationReport
entry_count integer · minimum 0 required

How many entries the submission held: every row in the file or line in the instruction set, whether it passed or not.

error_count integer · minimum 0 required

How many entries failed with an error. Each is named in entries with its position and why, so one submission tells you everything to fix.

warning_count integer · minimum 0 required

How many entries passed with a warning, each named in entries. A warning does not fail the entry; it points at something to confirm before you go ahead.

entries array of object optional

One item per error or warning found, with the row or line it sits on, its severity, a code and what was wrong. Empty or absent when every entry passed clean.

4 fields
index integer · minimum 1 required

The row in a file, or the line in an instruction set.

severity string required

error means the entry failed and must be fixed. warning means it passed, but points at something you should confirm before you go ahead.

errorwarning
code string required

Why the entry was flagged, as a code you can branch on. detail says the same in words and may change; the code does not.

detail string required

What was wrong with the entry, in plain words. Show it beside the row; it may change, so branch on code.

rejections array of object optional

One entry per refused reading, indexed against the submitted batch.

3 fields
index integer · minimum 1 required

Which reading was refused, as its position in the readings you sent. Counted from 1, so the first reading is 1.

reason string required

Why one reading in a batch was refused. A refused reading is named rather than dropped quietly, because a silently discarded position is a gap nobody can account for later.

outside_collection_windowimplausible_movementrecorded_in_the_futureolder_than_retention_windowduplicate_readingaccuracy_below_threshold
detail string optional

A sentence saying what about this reading was refused, where the reason code alone would not tell you. Not always present.

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.
404No record with that identifier.
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/allocation/heartbeats" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"worker_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","readings":[{"recorded_at":"2026-09-01T09:00:00Z","connectivity":"offline","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"availability":"available","battery_percent":0,"mock_location_detected":true,"device_integrity_attested":true}]}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationHeartbeatsCreate({
  idempotencyKey: crypto.randomUUID(),
  matchHeartbeatBatchRequest: {"workerId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","readings":[{"recordedAt":"2026-09-01T09:00:00Z","connectivity":"offline","position":{"latitude":-90,"longitude":-180,"accuracyMetres":0},"availability":"available","batteryPercent":0,"mockLocationDetected":true,"deviceIntegrityAttested":true}]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/heartbeats', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "readings": [
      {
        "recorded_at": "2026-09-01T09:00:00Z",
        "connectivity": "offline",
        "position": {
          "latitude": -90,
          "longitude": -180,
          "accuracy_metres": 0
        },
        "availability": "available",
        "battery_percent": 0,
        "mock_location_detected": true,
        "device_integrity_attested": true
      }
    ]
  }),
});
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.MATCHApi(client)

result = api.allocation_heartbeats_create(body={"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "readings": [{"recorded_at": "2026-09-01T09:00:00Z", "connectivity": "offline", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "availability": "available", "battery_percent": 0, "mock_location_detected": True, "device_integrity_attested": True}]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/heartbeats',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "readings": [{"recorded_at": "2026-09-01T09:00:00Z", "connectivity": "offline", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "availability": "available", "battery_percent": 0, "mock_location_detected": True, "device_integrity_attested": True}]},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationHeartbeatsCreate($idempotencyKey, json_decode('{"worker_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","readings":[{"recorded_at":"2026-09-01T09:00:00Z","connectivity":"offline","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"availability":"available","battery_percent":0,"mock_location_detected":true,"device_integrity_attested":true}]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/heartbeats');
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 => '{"worker_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","readings":[{"recorded_at":"2026-09-01T09:00:00Z","connectivity":"offline","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"availability":"available","battery_percent":0,"mock_location_detected":true,"device_integrity_attested":true}]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationHeartbeatsCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/heartbeats"))
    .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("""
        {
          "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "readings": [
            {
              "recorded_at": "2026-09-01T09:00:00Z",
              "connectivity": "offline",
              "position": {
                "latitude": -90,
                "longitude": -180,
                "accuracy_metres": 0
              },
              "availability": "available",
              "battery_percent": 0,
              "mock_location_detected": true,
              "device_integrity_attested": true
            }
          ]
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationHeartbeatsCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/heartbeats");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "readings": [
        {
          "recorded_at": "2026-09-01T09:00:00Z",
          "connectivity": "offline",
          "position": {
            "latitude": -90,
            "longitude": -180,
            "accuracy_metres": 0
          },
          "availability": "available",
          "battery_percent": 0,
          "mock_location_detected": true,
          "device_integrity_attested": true
        }
      ]
    }
    """, 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.MATCHAPI.AllocationHeartbeatsCreate(ctx).IdempotencyKey(key).MatchHeartbeatBatchRequest(body).Execute()
body := strings.NewReader(`{
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "readings": [
    {
      "recorded_at": "2026-09-01T09:00:00Z",
      "connectivity": "offline",
      "position": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      },
      "availability": "available",
      "battery_percent": 0,
      "mock_location_detected": true,
      "device_integrity_attested": true
    }
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/heartbeats", 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
{
  "object": "heartbeat_receipt",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "accepted_count": 0,
  "rejected_count": 0,
  "reconciled": true,
  "validation": {
    "entry_count": 0,
    "error_count": 0,
    "warning_count": 0,
    "entries": [
      {
        "index": 1,
        "severity": "error",
        "code": "no_payee_destination",
        "detail": "The payee has no verified destination, so this line cannot be paid."
      }
    ]
  },
  "zone": {
    "id": "zone_lagos_mainland_03",
    "name": "Lagos Mainland 03"
  },
  "presence": "reporting",
  "rejections": [
    {
      "index": 1,
      "reason": "outside_collection_window",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ]
}
GET/v1/allocation/supply_states#

List worker supply states

allocation.supply_states.list

See who is available, where they are reporting from, and who has gone quiet.

A worker whose heartbeats have stopped past the threshold is ghost supply. They drop out of ranking, and the availability they set is preserved, so one heartbeat brings them back. No re-registration is needed.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

availability string optional

Return only workers whose set state is available, unavailable or on_assignment, as they set it at PUT /v1/allocation/availability/{worker_id}. Leave it out to get workers in every state.

availableunavailableon_assignment
presence string optional

Return only workers in one presence: reporting while heartbeats arrive, stale once they have lapsed, ghost once silence passes ghost_threshold_seconds. Leave it out to get every worker.

reportingstaleghost
zone string optional

Return only workers who resolved to one operational zone, by its id such as zone_lagos_mainland_03, as it reads in zone.id on a supply state or a heartbeat receipt. Leave it out to get workers in every zone.

Returns

A page of supply states.

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 SupplyState required

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

15 fields of SupplyState
object always "supply_state" required

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

worker_id string required

The worker this state describes: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR. Pass it as {worker_id} to retrieve one state or to set availability.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

availability string required

Set by the worker and never inferred. Neither movement nor silence changes it.

availableunavailableon_assignment
presence string required

What was last heard from the worker, which is separate from what they set. A worker can be available and absent at the same time. That combination is ghost supply.

reportingstaleghost
rankable boolean required

False for ghost supply and for a worker suspended by an upheld integrity flag. The availability they set is untouched either way.

excluded_because string · nullable optional

Why the worker is not rankable: ghost_supply (silent past the threshold), integrity_flag_upheld (suspended by an upheld flag), no_executed_engagement (nothing executed in RAIL) or passport_not_live. null while rankable is true.

ghost_supplyintegrity_flag_upheldno_executed_engagementpassport_not_livenull
position one of optional

Null whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.

GeoPointor
position_withheld_because string · nullable optional

Which rule closed the collection window, so a missing position is provable: availability_not_set (never set by the worker), worker_unavailable (they set unavailable) or no_active_assignment. null while the window is open.

availability_not_setworker_unavailableno_active_assignmentnull
collection_window string optional

Whether positions are being collected right now: open only while the worker is available or on_assignment, closed otherwise. While closed, position is null and position_withheld_because names the rule.

openclosed
zone one of optional

The operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.

Zoneor
last_heartbeat_at string · date-time · nullable optional

The worker's last heartbeat, as an RFC 3339 timestamp in UTC, or null when none has arrived. Silence past ghost_threshold_seconds from here makes an available worker ghost supply.

ghost_threshold_seconds integer optional

How long without a heartbeat before an available worker becomes ghost supply.

availability_set_at string · date-time · nullable optional

When the worker last set their availability. Preserved across a ghost exclusion.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
const result = await api.allocationSupplyStatesList({ limit: 25, availability: 'available', presence: 'reporting', zone: 'zone_lagos_mainland_03' });
// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
result = api.allocation_supply_states_list(limit=25, availability='available', presence='reporting', zone='zone_lagos_mainland_03')
import os

import requests

# query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
$result = $api->allocationSupplyStatesList(limit: 25, availability: 'available', presence: 'reporting', zone: 'zone_lagos_mainland_03');
<?php
// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
var result = api.allocationSupplyStatesList(25, null, MatchAvailabilityState.fromValue("available"), MatchPresence.fromValue("reporting"), "zone_lagos_mainland_03");
// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
var result = api.AllocationSupplyStatesList(limit: 25, availability: MatchAvailabilityState.Available, presence: MatchPresence.Reporting, zone: "zone_lagos_mainland_03");
// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03");
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), starting_after (optional), availability (optional), presence (optional), zone (optional)
result, _, err := client.MATCHAPI.AllocationSupplyStatesList(ctx).Limit(25).Availability(droomwork.MatchAvailabilityState("available")).Presence(droomwork.MatchPresence("reporting")).Zone("zone_lagos_mainland_03").Execute()
// query parameters: limit (optional), starting_after (optional), availability (optional), presence (optional), zone (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/supply_states?limit=25&availability=available&presence=reporting&zone=zone_lagos_mainland_03", 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": [
    {
      "object": "supply_state",
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "livemode": true,
      "mocked": true,
      "availability": "available",
      "presence": "reporting",
      "rankable": true,
      "excluded_because": "ghost_supply",
      "position": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      },
      "position_withheld_because": "availability_not_set",
      "collection_window": "open",
      "zone": {
        "id": "zone_lagos_mainland_03",
        "name": "Lagos Mainland 03"
      },
      "last_heartbeat_at": "2026-09-01T09:00:00Z",
      "ghost_threshold_seconds": 1,
      "availability_set_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
GET/v1/allocation/supply_states/{worker_id}#

Retrieve one worker's supply state

allocation.supply_states.retrieve

Read what this worker set, what was last heard from them, and whether they can currently be ranked.

When the collection window is closed the position is null and position_withheld_because tells you which rule closed it. You can check from the data that location is collected only during availability or assignment.

Path parameters

worker_id string required

The worker's sub_ subject reference: the subject_ref you chose and sent at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR, as it reads in worker_id on each state at GET /v1/allocation/supply_states.

Returns

The supply state.

object always "supply_state" required

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

worker_id string required

The worker this state describes: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR. Pass it as {worker_id} to retrieve one state or to set availability.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

availability string required

Set by the worker and never inferred. Neither movement nor silence changes it.

availableunavailableon_assignment
presence string required

What was last heard from the worker, which is separate from what they set. A worker can be available and absent at the same time. That combination is ghost supply.

reportingstaleghost
rankable boolean required

False for ghost supply and for a worker suspended by an upheld integrity flag. The availability they set is untouched either way.

excluded_because string · nullable optional

Why the worker is not rankable: ghost_supply (silent past the threshold), integrity_flag_upheld (suspended by an upheld flag), no_executed_engagement (nothing executed in RAIL) or passport_not_live. null while rankable is true.

ghost_supplyintegrity_flag_upheldno_executed_engagementpassport_not_livenull
position one of optional

Null whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.

GeoPointor
position_withheld_because string · nullable optional

Which rule closed the collection window, so a missing position is provable: availability_not_set (never set by the worker), worker_unavailable (they set unavailable) or no_active_assignment. null while the window is open.

availability_not_setworker_unavailableno_active_assignmentnull
collection_window string optional

Whether positions are being collected right now: open only while the worker is available or on_assignment, closed otherwise. While closed, position is null and position_withheld_because names the rule.

openclosed
zone one of optional

The operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.

Zoneor
last_heartbeat_at string · date-time · nullable optional

The worker's last heartbeat, as an RFC 3339 timestamp in UTC, or null when none has arrived. Silence past ghost_threshold_seconds from here makes an available worker ghost supply.

ghost_threshold_seconds integer optional

How long without a heartbeat before an available worker becomes ghost supply.

availability_set_at string · date-time · nullable optional

When the worker last set their availability. Preserved across a ghost exclusion.

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 GET "https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationSupplyStatesRetrieve({ workerId: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

result = api.allocation_supply_states_retrieve(worker_id='sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationSupplyStatesRetrieve(worker_id: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;

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

var result = api.allocationSupplyStatesRetrieve("sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

var result = api.AllocationSupplyStatesRetrieve(workerId: "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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.MATCHAPI.AllocationSupplyStatesRetrieve(ctx, "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/supply_states/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "supply_state",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "livemode": true,
  "mocked": true,
  "availability": "available",
  "presence": "reporting",
  "rankable": true,
  "excluded_because": "ghost_supply",
  "position": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "position_withheld_because": "availability_not_set",
  "collection_window": "open",
  "zone": {
    "id": "zone_lagos_mainland_03",
    "name": "Lagos Mainland 03"
  },
  "last_heartbeat_at": "2026-09-01T09:00:00Z",
  "ghost_threshold_seconds": 1,
  "availability_set_at": "2026-09-01T09:00:00Z"
}
PUT/v1/allocation/availability/{worker_id}#

Set a worker's availability

allocation.availability.update

The worker sets their own availability here. This is the only way it changes.

It is never derived from movement, from a heartbeat or from silence. A worker who stops reporting becomes ghost supply and drops out of ranking, and the state they set is preserved untouched, so recovery restores it.

Setting unavailable closes the location collection window. Positions stop being accepted from that moment.

Path parameters

worker_id string required

The worker's sub_ subject reference: the subject_ref you chose and sent at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR, as it reads in worker_id on each state at GET /v1/allocation/supply_states.

Body

availability string required

Set by the worker and never inferred. Neither movement nor silence changes it.

availableunavailableon_assignment
effective_at string · date-time optional

Defaults to now. A future time schedules the change rather than applying it.

Returns

The supply state, carrying the availability now in force.

object always "supply_state" required

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

worker_id string required

The worker this state describes: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR. Pass it as {worker_id} to retrieve one state or to set availability.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

availability string required

Set by the worker and never inferred. Neither movement nor silence changes it.

availableunavailableon_assignment
presence string required

What was last heard from the worker, which is separate from what they set. A worker can be available and absent at the same time. That combination is ghost supply.

reportingstaleghost
rankable boolean required

False for ghost supply and for a worker suspended by an upheld integrity flag. The availability they set is untouched either way.

excluded_because string · nullable optional

Why the worker is not rankable: ghost_supply (silent past the threshold), integrity_flag_upheld (suspended by an upheld flag), no_executed_engagement (nothing executed in RAIL) or passport_not_live. null while rankable is true.

ghost_supplyintegrity_flag_upheldno_executed_engagementpassport_not_livenull
position one of optional

Null whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.

GeoPointor
position_withheld_because string · nullable optional

Which rule closed the collection window, so a missing position is provable: availability_not_set (never set by the worker), worker_unavailable (they set unavailable) or no_active_assignment. null while the window is open.

availability_not_setworker_unavailableno_active_assignmentnull
collection_window string optional

Whether positions are being collected right now: open only while the worker is available or on_assignment, closed otherwise. While closed, position is null and position_withheld_because names the rule.

openclosed
zone one of optional

The operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.

Zoneor
last_heartbeat_at string · date-time · nullable optional

The worker's last heartbeat, as an RFC 3339 timestamp in UTC, or null when none has arrived. Silence past ghost_threshold_seconds from here makes an available worker ghost supply.

ghost_threshold_seconds integer optional

How long without a heartbeat before an available worker becomes ghost supply.

availability_set_at string · date-time · nullable optional

When the worker last set their availability. Preserved across a ghost exclusion.

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.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X PUT "https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"availability":"available","effective_at":"2026-09-01T09:00:00Z"}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAvailabilityUpdate({
  workerId: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  matchAvailabilityUpdateRequest: {"availability":"available","effectiveAt":"2026-09-01T09:00:00Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  method: 'PUT',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "availability": "available",
    "effective_at": "2026-09-01T09:00:00Z"
  }),
});
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.MATCHApi(client)

result = api.allocation_availability_update(worker_id='sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"availability": "available", "effective_at": "2026-09-01T09:00:00Z"})
import os

import requests

response = requests.request(
    'PUT',
    'https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
    json={"availability": "available", "effective_at": "2026-09-01T09:00:00Z"},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAvailabilityUpdate(json_decode('{"availability":"available","effective_at":"2026-09-01T09:00:00Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'PUT',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{"availability":"available","effective_at":"2026-09-01T09:00:00Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationAvailabilityUpdate("sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .method("PUT", HttpRequest.BodyPublishers.ofString("""
        {
          "availability": "available",
          "effective_at": "2026-09-01T09:00:00Z"
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationAvailabilityUpdate(workerId: "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Put, "https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Content = new StringContent("""
    {
      "availability": "available",
      "effective_at": "2026-09-01T09:00:00Z"
    }
    """, 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.MATCHAPI.AllocationAvailabilityUpdate(ctx, "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").MatchAvailabilityUpdateRequest(body).Execute()
body := strings.NewReader(`{
  "availability": "available",
  "effective_at": "2026-09-01T09:00:00Z"
}`)
req, _ := http.NewRequest("PUT", "https://sandbox.droomwork.io/v1/allocation/availability/sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "supply_state",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "livemode": true,
  "mocked": true,
  "availability": "available",
  "presence": "reporting",
  "rankable": true,
  "excluded_because": "ghost_supply",
  "position": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "position_withheld_because": "availability_not_set",
  "collection_window": "open",
  "zone": {
    "id": "zone_lagos_mainland_03",
    "name": "Lagos Mainland 03"
  },
  "last_heartbeat_at": "2026-09-01T09:00:00Z",
  "ghost_threshold_seconds": 1,
  "availability_set_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/integrity_flags#

List position integrity flags

allocation.integrity_flags.list

List suspected position spoofing, detected across mock location signals, device integrity attestation, implausible movement and a network cross check.

No single signal suspends a worker. A flag carries every signal that fired, and one signal on its own can never set suspends to true. A person decides, through the adjudicate endpoint.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Return only flags in one state: open while nobody has decided, upheld or dismissed once a person has decided at POST /v1/allocation/integrity_flags/{integrity_flag_id}/adjudicate. Leave it out to get every flag.

openuphelddismissed
worker_id string optional

Return only flags raised against one worker, by the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens when you first recorded them with ANCHOR. Leave it out to get flags for every worker.

Returns

A page of integrity flags.

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 IntegrityFlag required

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

10 fields of IntegrityFlag
id string required

The flag's identifier, as it reads in id on each flag at GET /v1/allocation/integrity_flags. It starts with match_geo_aware_workforce_orchestrator_integrity_flag_, never changes, and is the integrity_flag_id you pass to adjudicate.

object always "integrity_flag" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

worker_id string required

The worker the flag is against: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens in ANCHOR. While an upheld flag suspends them, their supply state's excluded_because is integrity_flag_upheld.

status string required
openuphelddismissed
signals array of IntegritySignal required

Every detection that fired, at least one: its source, when it fired and what it saw. suspends can only be true when more than one is here.

3 fields of IntegritySignal
source string required

The four independent detections. Agreement between them is what a flag rests on.

mock_locationdevice_integrityimplausible_movementnetwork_cross_check
fired_at string · date-time required

When this detection fired, as an RFC 3339 timestamp in UTC. Not the flag's raised_at: each signal on a flag carries its own time.

detail string required

What the signal saw, in terms a person adjudicating can weigh.

suspends boolean required

True only where more than one independent signal fired and a person upheld the flag. One signal on its own suspends nobody.

decision one of optional

The adjudication, once a person has made one: the outcome, who decided, when, their reason and the signals they accepted. null while the flag is open; once set it is never edited.

Decisionor
raised_at string · date-time optional

When the flag was raised, as an RFC 3339 timestamp in UTC. The signals it carries each have their own fired_at.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
const result = await api.allocationIntegrityFlagsList({ limit: 25, status: 'open', workerId: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
result = api.allocation_integrity_flags_list(limit=25, status='open', worker_id='sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
$result = $api->allocationIntegrityFlagsList(limit: 25, status: 'open', worker_id: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
var result = api.allocationIntegrityFlagsList(25, null, MatchIntegrityFlagStatus.fromValue("open"), "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
var result = api.AllocationIntegrityFlagsList(limit: 25, status: MatchIntegrityFlagStatus.Open, workerId: "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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), starting_after (optional), status (optional), worker_id (optional)
result, _, err := client.MATCHAPI.AllocationIntegrityFlagsList(ctx).Limit(25).Status(droomwork.MatchIntegrityFlagStatus("open")).WorkerId("sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
// query parameters: limit (optional), starting_after (optional), status (optional), worker_id (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/integrity_flags?limit=25&status=open&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "integrity_flag",
      "livemode": true,
      "mocked": true,
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "status": "open",
      "signals": [
        {
          "source": "mock_location",
          "fired_at": "2026-09-01T09:00:00Z",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
      ],
      "suspends": true,
      "decision": {
        "outcome": "upheld",
        "decided_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
        "decided_at": "2026-09-01T09:00:00Z",
        "reason": "The requester confirmed the work in person.",
        "accepted_signals": [
          "example"
        ]
      },
      "raised_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
POST/v1/allocation/integrity_flags/{integrity_flag_id}/adjudicate#

Adjudicate an integrity flag

allocation.integrity_flags.adjudicate

Decide whether the signals amount to spoofing. Uphold the flag and the worker is suspended from ranking. Dismiss it and the flag clears and they return.

The decision, who made it and the reason are recorded on the flag and can't be edited afterwards. If you later disagree, make a new adjudication; this one stays as it is.

Path parameters

integrity_flag_id string required

The flag's identifier, from id on a flag you listed at GET /v1/allocation/integrity_flags. It starts with match_geo_aware_workforce_orchestrator_integrity_flag_.

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

outcome string required

Your decision: upheld or dismissed. On a flag, upheld suspends the worker where more than one signal fired and dismissed returns them; on an exception, upheld seals the proof and dismissed closes the assignment without one.

uphelddismissed
reason string required

Why you decided as you did, in your own words, at least one character. It is recorded with the decision and can't be edited afterwards.

accepted_signals array of string optional

Name the signals you accepted, when only some persuaded you: by source from a flag's signals, or by signal from an exception's conflicting_signals. Recorded with the decision.

Returns

The adjudicated flag.

id string required

The flag's identifier, as it reads in id on each flag at GET /v1/allocation/integrity_flags. It starts with match_geo_aware_workforce_orchestrator_integrity_flag_, never changes, and is the integrity_flag_id you pass to adjudicate.

object always "integrity_flag" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

worker_id string required

The worker the flag is against: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens in ANCHOR. While an upheld flag suspends them, their supply state's excluded_because is integrity_flag_upheld.

status string required
openuphelddismissed
signals array of IntegritySignal required

Every detection that fired, at least one: its source, when it fired and what it saw. suspends can only be true when more than one is here.

3 fields of IntegritySignal
source string required

The four independent detections. Agreement between them is what a flag rests on.

mock_locationdevice_integrityimplausible_movementnetwork_cross_check
fired_at string · date-time required

When this detection fired, as an RFC 3339 timestamp in UTC. Not the flag's raised_at: each signal on a flag carries its own time.

detail string required

What the signal saw, in terms a person adjudicating can weigh.

suspends boolean required

True only where more than one independent signal fired and a person upheld the flag. One signal on its own suspends nobody.

decision one of optional

The adjudication, once a person has made one: the outcome, who decided, when, their reason and the signals they accepted. null while the flag is open; once set it is never edited.

Decisionor
raised_at string · date-time optional

When the flag was raised, as an RFC 3339 timestamp in UTC. The signals it carries each have their own fired_at.

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.
404No record with that identifier.
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/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationIntegrityFlagsAdjudicate({
  integrityFlagId: 'match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchAdjudicateRequest: {"outcome":"upheld","reason":"The requester confirmed the work in person.","acceptedSignals":["example"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "outcome": "upheld",
    "reason": "The requester confirmed the work in person.",
    "accepted_signals": [
      "example"
    ]
  }),
});
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.MATCHApi(client)

result = api.allocation_integrity_flags_adjudicate(integrity_flag_id='match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"outcome": "upheld", "reason": "The requester confirmed the work in person.", "accepted_signals": ["example"]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"outcome": "upheld", "reason": "The requester confirmed the work in person.", "accepted_signals": ["example"]},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationIntegrityFlagsAdjudicate($idempotencyKey, json_decode('{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate');
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 => '{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationIntegrityFlagsAdjudicate("match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate"))
    .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("""
        {
          "outcome": "upheld",
          "reason": "The requester confirmed the work in person.",
          "accepted_signals": [
            "example"
          ]
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationIntegrityFlagsAdjudicate(integrityFlagId: "match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "outcome": "upheld",
      "reason": "The requester confirmed the work in person.",
      "accepted_signals": [
        "example"
      ]
    }
    """, 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.MATCHAPI.AllocationIntegrityFlagsAdjudicate(ctx, "match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchAdjudicateRequest(body).Execute()
body := strings.NewReader(`{
  "outcome": "upheld",
  "reason": "The requester confirmed the work in person.",
  "accepted_signals": [
    "example"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/integrity_flags/match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate", 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": "match_geo_aware_workforce_orchestrator_integrity_flag_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "integrity_flag",
  "livemode": true,
  "mocked": true,
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "open",
  "signals": [
    {
      "source": "mock_location",
      "fired_at": "2026-09-01T09:00:00Z",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "suspends": true,
  "decision": {
    "outcome": "upheld",
    "decided_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "decided_at": "2026-09-01T09:00:00Z",
    "reason": "The requester confirmed the work in person.",
    "accepted_signals": [
      "example"
    ]
  },
  "raised_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/work_orders#

List work orders

allocation.work_orders.list

Lists your work orders, newest first.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Return only orders in one state: open when created, offered once offers went out, filled once you recorded the headcount met, cancelled once you withdrew it, unfilled on expiry or cascade exhaustion. Leave it out to get every state.

openofferedfilledcancelledunfilled
occupation_code string optional

Return only orders for one occupation, by the taxonomy code you sent as occupation_code at POST /v1/allocation/work_orders, such as ng-7412. Leave it out to get orders for every occupation.

created_after string optional

Return only orders whose created_at is after this moment, as an RFC 3339 timestamp in UTC. Pair it with created_before to bound a window; leave it out for no earliest bound.

created_before string optional

Return only orders whose created_at is before this moment, as an RFC 3339 timestamp in UTC. Pair it with created_after to bound a window; leave it out for no latest bound.

Returns

A page of work orders.

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 WorkOrder required

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

21 fields of WorkOrder
id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
const result = await api.allocationWorkOrdersList({ limit: 25, status: 'open', occupationCode: 'ng-7412', createdAfter: '2026-01-01T00:00:00Z', createdBefore: '2027-01-01T00:00:00Z' });
// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
result = api.allocation_work_orders_list(limit=25, status='open', occupation_code='ng-7412', created_after='2026-01-01T00:00:00Z', created_before='2027-01-01T00:00:00Z')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
$result = $api->allocationWorkOrdersList(limit: 25, status: 'open', occupation_code: 'ng-7412', created_after: '2026-01-01T00:00:00Z', created_before: '2027-01-01T00:00:00Z');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
var result = api.allocationWorkOrdersList(25, null, MatchWorkOrderStatus.fromValue("open"), "ng-7412", "2026-01-01T00:00:00Z", "2027-01-01T00:00:00Z");
// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
var result = api.AllocationWorkOrdersList(limit: 25, status: MatchWorkOrderStatus.Open, occupationCode: "ng-7412", createdAfter: "2026-01-01T00:00:00Z", createdBefore: "2027-01-01T00:00:00Z");
// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z");
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), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
result, _, err := client.MATCHAPI.AllocationWorkOrdersList(ctx).Limit(25).Status(droomwork.MatchWorkOrderStatus("open")).OccupationCode("ng-7412").CreatedAfter("2026-01-01T00:00:00Z").CreatedBefore("2027-01-01T00:00:00Z").Execute()
// query parameters: limit (optional), starting_after (optional), status (optional), occupation_code (optional), created_after (optional), created_before (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/work_orders?limit=25&status=open&occupation_code=ng-7412&created_after=2026-01-01T00%3A00%3A00Z&created_before=2027-01-01T00%3A00%3A00Z", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "work_order",
      "livemode": true,
      "mocked": true,
      "status": "open",
      "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "service_category": "example",
      "occupation_code": "ng-7412",
      "headcount": 1,
      "urgency_class": "standard",
      "location": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      },
      "time_window": {
        "starts_at": "2026-09-01T09:00:00Z",
        "ends_at": "2026-09-01T09:00:00Z"
      },
      "expires_at": "2026-09-01T09:00:00Z",
      "headcount_filled": 0,
      "location_description": "example",
      "constraints": [
        {
          "code": "licence_electrical",
          "kind": "hard",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
      ],
      "relaxations": [
        {
          "preference_code": "no_payee_destination",
          "round": 1,
          "relaxed_at": "2026-09-01T09:00:00Z",
          "reason": "pool_exhausted"
        }
      ],
      "escalation_bounds": {
        "initial_radius_metres": 0,
        "maximum_radius_metres": 0,
        "step_metres": 1
      },
      "budget_ceiling": {
        "amount": 1234567,
        "currency": "NGN"
      },
      "cancelled_reason": "example",
      "created_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
POST/v1/allocation/work_orders#

Open a work order

allocation.work_orders.create

Open one work order for the demand, however it reached you. Send the service category, the occupation code, the location, the time window, the urgency class, the headcount, the hard constraints, the soft preferences, the budget ceiling, the expiry and the requester it belongs to.

Hard constraints are never relaxed. Where the cascade relaxes a soft preference to widen the pool, the relaxation is recorded on the order with the round it happened in, so you can explain a later ranking.

The requester must be verified through ANCHOR before an order opens. Without that the request is refused, and the readiness endpoint tells you what is missing.

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

requester_id string required

Who the work is for: the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens and verified at POST /v1/identity/verifications. Refused with 422 subject_not_anchored until ANCHOR has verified them.

service_category string required

The kind of service the work is, such as field_maintenance, carried on the order as you sent it. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. If the order goes unfilled, the supply gap sent to FLOW names this occupation.

headcount integer · minimum 1 required

How many workers you need, at least 1. The order stays open until this many acceptances are held; if it lapses short of that, it's declared unfilled with a reason.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional, and it never replaces location.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each with a code and a kind. A hard one is never relaxed; a soft one may be dropped to widen the pool, and the relaxation is recorded on the order.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

The moment the order lapses, as an RFC 3339 timestamp in UTC. If the headcount is unmet by then the order is declared unfilled with a reason and the shortfall raised as a supply gap.

Returns

The work order, open and ready to cascade.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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

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.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/allocation/work_orders" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"requester_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","service_category":"example","occupation_code":"ng-7412","headcount":1,"urgency_class":"standard","location":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"time_window":{"starts_at":"2026-09-01T09:00:00Z","ends_at":"2026-09-01T09:00:00Z"},"expires_at":"2026-09-01T09:00:00Z","location_description":"example","constraints":[{"code":"licence_electrical","kind":"hard","detail":"The payee has no verified destination, so this line cannot be paid."}],"escalation_bounds":{"initial_radius_metres":0,"maximum_radius_metres":0,"step_metres":1},"budget_ceiling":{"amount":1234567,"currency":"NGN"}}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationWorkOrdersCreate({
  idempotencyKey: crypto.randomUUID(),
  matchWorkOrderCreateRequest: {"requesterId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","serviceCategory":"example","occupationCode":"ng-7412","headcount":1,"urgencyClass":"standard","location":{"latitude":-90,"longitude":-180,"accuracyMetres":0},"timeWindow":{"startsAt":"2026-09-01T09:00:00Z","endsAt":"2026-09-01T09:00:00Z"},"expiresAt":"2026-09-01T09:00:00Z","locationDescription":"example","constraints":[{"code":"licence_electrical","kind":"hard","detail":"The payee has no verified destination, so this line cannot be paid."}],"escalationBounds":{"initialRadiusMetres":0,"maximumRadiusMetres":0,"stepMetres":1},"budgetCeiling":{"amount":1234567,"currency":"NGN"}},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "service_category": "example",
    "occupation_code": "ng-7412",
    "headcount": 1,
    "urgency_class": "standard",
    "location": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "time_window": {
      "starts_at": "2026-09-01T09:00:00Z",
      "ends_at": "2026-09-01T09:00:00Z"
    },
    "expires_at": "2026-09-01T09:00:00Z",
    "location_description": "example",
    "constraints": [
      {
        "code": "licence_electrical",
        "kind": "hard",
        "detail": "The payee has no verified destination, so this line cannot be paid."
      }
    ],
    "escalation_bounds": {
      "initial_radius_metres": 0,
      "maximum_radius_metres": 0,
      "step_metres": 1
    },
    "budget_ceiling": {
      "amount": 1234567,
      "currency": "NGN"
    }
  }),
});
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.MATCHApi(client)

result = api.allocation_work_orders_create(body={"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "service_category": "example", "occupation_code": "ng-7412", "headcount": 1, "urgency_class": "standard", "location": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "time_window": {"starts_at": "2026-09-01T09:00:00Z", "ends_at": "2026-09-01T09:00:00Z"}, "expires_at": "2026-09-01T09:00:00Z", "location_description": "example", "constraints": [{"code": "licence_electrical", "kind": "hard", "detail": "The payee has no verified destination, so this line cannot be paid."}], "escalation_bounds": {"initial_radius_metres": 0, "maximum_radius_metres": 0, "step_metres": 1}, "budget_ceiling": {"amount": 1234567, "currency": "NGN"}})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/work_orders',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "service_category": "example", "occupation_code": "ng-7412", "headcount": 1, "urgency_class": "standard", "location": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "time_window": {"starts_at": "2026-09-01T09:00:00Z", "ends_at": "2026-09-01T09:00:00Z"}, "expires_at": "2026-09-01T09:00:00Z", "location_description": "example", "constraints": [{"code": "licence_electrical", "kind": "hard", "detail": "The payee has no verified destination, so this line cannot be paid."}], "escalation_bounds": {"initial_radius_metres": 0, "maximum_radius_metres": 0, "step_metres": 1}, "budget_ceiling": {"amount": 1234567, "currency": "NGN"}},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersCreate($idempotencyKey, json_decode('{"requester_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","service_category":"example","occupation_code":"ng-7412","headcount":1,"urgency_class":"standard","location":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"time_window":{"starts_at":"2026-09-01T09:00:00Z","ends_at":"2026-09-01T09:00:00Z"},"expires_at":"2026-09-01T09:00:00Z","location_description":"example","constraints":[{"code":"licence_electrical","kind":"hard","detail":"The payee has no verified destination, so this line cannot be paid."}],"escalation_bounds":{"initial_radius_metres":0,"maximum_radius_metres":0,"step_metres":1},"budget_ceiling":{"amount":1234567,"currency":"NGN"}}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders');
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 => '{"requester_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","service_category":"example","occupation_code":"ng-7412","headcount":1,"urgency_class":"standard","location":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"time_window":{"starts_at":"2026-09-01T09:00:00Z","ends_at":"2026-09-01T09:00:00Z"},"expires_at":"2026-09-01T09:00:00Z","location_description":"example","constraints":[{"code":"licence_electrical","kind":"hard","detail":"The payee has no verified destination, so this line cannot be paid."}],"escalation_bounds":{"initial_radius_metres":0,"maximum_radius_metres":0,"step_metres":1},"budget_ceiling":{"amount":1234567,"currency":"NGN"}}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationWorkOrdersCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders"))
    .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("""
        {
          "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "service_category": "example",
          "occupation_code": "ng-7412",
          "headcount": 1,
          "urgency_class": "standard",
          "location": {
            "latitude": -90,
            "longitude": -180,
            "accuracy_metres": 0
          },
          "time_window": {
            "starts_at": "2026-09-01T09:00:00Z",
            "ends_at": "2026-09-01T09:00:00Z"
          },
          "expires_at": "2026-09-01T09:00:00Z",
          "location_description": "example",
          "constraints": [
            {
              "code": "licence_electrical",
              "kind": "hard",
              "detail": "The payee has no verified destination, so this line cannot be paid."
            }
          ],
          "escalation_bounds": {
            "initial_radius_metres": 0,
            "maximum_radius_metres": 0,
            "step_metres": 1
          },
          "budget_ceiling": {
            "amount": 1234567,
            "currency": "NGN"
          }
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/work_orders");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "service_category": "example",
      "occupation_code": "ng-7412",
      "headcount": 1,
      "urgency_class": "standard",
      "location": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      },
      "time_window": {
        "starts_at": "2026-09-01T09:00:00Z",
        "ends_at": "2026-09-01T09:00:00Z"
      },
      "expires_at": "2026-09-01T09:00:00Z",
      "location_description": "example",
      "constraints": [
        {
          "code": "licence_electrical",
          "kind": "hard",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
      ],
      "escalation_bounds": {
        "initial_radius_metres": 0,
        "maximum_radius_metres": 0,
        "step_metres": 1
      },
      "budget_ceiling": {
        "amount": 1234567,
        "currency": "NGN"
      }
    }
    """, 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.MATCHAPI.AllocationWorkOrdersCreate(ctx).IdempotencyKey(key).MatchWorkOrderCreateRequest(body).Execute()
body := strings.NewReader(`{
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  }
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/work_orders", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/work_orders/{work_order_id}#

Retrieve a work order

allocation.work_orders.retrieve

Read the order as it now stands, including every soft preference the cascade relaxed and the round it was relaxed in.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

Returns

The work order.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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 GET "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationWorkOrdersRetrieve({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

result = api.allocation_work_orders_retrieve(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersRetrieve(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;

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

var result = api.allocationWorkOrdersRetrieve("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersRetrieve(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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.MATCHAPI.AllocationWorkOrdersRetrieve(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/work_orders/{work_order_id}/cancel#

Cancel a work order

allocation.work_orders.cancel

Withdraw the order from every live cascade. Outstanding offers are withdrawn with the reason work_order_cancelled, one of the fixed reasons a worker sees.

An order that has already been filled can't be cancelled here. Cancel the assignment instead.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

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

reason string required

Why you are cancelling: requester_withdrew, no_longer_required, raised_in_error or worker_removed_from_cascade (the worker lost standing in ANCHOR or RAIL). It reaches the worker where one was offered or assigned.

requester_withdrewno_longer_requiredraised_in_errorworker_removed_from_cascade
detail string optional

More about why, in your own words, for whoever reads the record later. Optional; reason is the fixed code.

Returns

The cancelled work order.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
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/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationWorkOrdersCancel({
  workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchCancellationRequest: {"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "requester_withdrew",
    "detail": "The payee has no verified destination, so this line cannot be paid."
  }),
});
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.MATCHApi(client)

result = api.allocation_work_orders_cancel(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"reason": "requester_withdrew", "detail": "The payee has no verified destination, so this line cannot be paid."})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"reason": "requester_withdrew", "detail": "The payee has no verified destination, so this line cannot be paid."},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersCancel($idempotencyKey, json_decode('{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel');
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 => '{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationWorkOrdersCancel("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel"))
    .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("""
        {
          "reason": "requester_withdrew",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersCancel(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "reason": "requester_withdrew",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
    """, 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.MATCHAPI.AllocationWorkOrdersCancel(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchCancellationRequest(body).Execute()
body := strings.NewReader(`{
  "reason": "requester_withdrew",
  "detail": "The payee has no verified destination, so this line cannot be paid."
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/work_orders/{work_order_id}/ranking#

Retrieve the ranking for a work order

allocation.rankings.retrieve

Read the ranking in full. Every candidate the gates considered, whether each one passed, every component score for those that did, the weight set version and the outcome.

Candidates are ranked on travel time over the road network rather than straight line distance, competence evidence from PROOF, reliability history, fatigue and cost. A candidate excluded by a gate carries the gate that excluded them and no scores. An ineligible worker is never scored.

Fatigue and hours limits are hard gates. An allocation that would breach one is never produced, so you will not see one flagged.

A re-rank after a decline or a timeout appends a round. Earlier rounds stay readable, so you can reconstruct who was asked and in what order.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

Query parameters

round integer optional

One pass of the cascade, counted from 1: the round on each entry of the ranking's rounds, and on each offer from GET /v1/allocation/work_orders/{work_order_id}/offers. Leave it out to get every round.

Returns

The ranking attempt.

object always "ranking" required

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

work_order_id string required

The id of the work order this ranking is for, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. One ranking per order, with a round appended on each re-rank.

weight_set_version string required

The version of the weights in force when this ranking ran.

rounds array of RankingRound required

Every pass of the cascade in order from round 1, each with every candidate considered, the radius it searched and whether it widened. Pass round on the request to read one pass only.

5 fields of RankingRound
round integer · minimum 1 required

Which pass of the cascade this is, counted from 1. A decline or a timeout opens the next, and offers and relaxations carry the same number so you can line them up.

ranked_at string · date-time required

When this pass ranked the pool, as an RFC 3339 timestamp in UTC. Supply as it stood at that moment is what was ranked, not the pool an earlier pass saw.

radius_metres integer · minimum 0 required

Within the bounds published on the work order.

escalated boolean optional

True where this round widened the radius over the previous one.

candidates array of RankingCandidate required

Every candidate considered, eligible or not. Stored in full.

8 fields of RankingCandidate
worker_id string required

The worker this candidate is, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. The same identifier you read their supply state with at GET /v1/allocation/supply_states/{worker_id}.

eligible boolean required

true when the worker passed every gate and was scored. false when a gate excluded them: failed_gates names it, and no score is present.

failed_gates array of EligibilityGate optional

Empty for an eligible candidate.

components one of optional

The five scores this candidate was ranked on: travel time, competence, reliability, fatigue and cost. Null for an ineligible candidate, who is never scored.

ScoreComponentsor
total_score number · nullable optional

The candidate's overall score, combining the five components under the weight set the ranking names. Null for an ineligible candidate.

rank integer · nullable · minimum 1 optional

The candidate's position in this round, 1 being the top. Null for an ineligible candidate.

offered boolean optional

true when this round made the worker an offer, false when it did not. Where it is false, non_offer_reason says why.

non_offer_reason one of optional

Why this worker got no offer in this round: a code from a closed set, with the wording the worker sees. Null where they were offered.

NonOfferReasonor
surge object optional

Surge index and incentive targeting are deferred. The values here are placeholders and carry mocked true even in the live realm.

2 fields
mocked always true required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

index number required

The surge index, as a placeholder. Surge and incentive targeting are deferred, so this value is mocked even in the live realm; don't act on it.

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?
# query parameters: round (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: round (optional)
const result = await api.allocationRankingsRetrieve({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', round: 1 });
// query parameters: round (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1', {
  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.MATCHApi(client)

# query parameters: round (optional)
result = api.allocation_rankings_retrieve(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', round=1)
import os

import requests

# query parameters: round (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: round (optional)
$result = $api->allocationRankingsRetrieve(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', round: 1);
<?php
// query parameters: round (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1');
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.MatchApi;

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

// query parameters: round (optional)
var result = api.allocationRankingsRetrieve("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 1);
// query parameters: round (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1"))
    .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 MATCHApi(config);

// query parameters: round (optional)
var result = api.AllocationRankingsRetrieve(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", round: 1);
// query parameters: round (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1");
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: round (optional)
result, _, err := client.MATCHAPI.AllocationRankingsRetrieve(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Round(1).Execute()
// query parameters: round (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/ranking?round=1", 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": "ranking",
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "weight_set_version": "2026.08.1",
  "rounds": [
    {
      "round": 1,
      "ranked_at": "2026-09-01T09:00:00Z",
      "radius_metres": 0,
      "candidates": [
        {
          "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "eligible": true,
          "failed_gates": [
            "passport_live"
          ],
          "components": {
            "travel_time": {
              "seconds": 0,
              "score": 1,
              "route_distance_metres": 0
            },
            "competence": {
              "score": 1,
              "proof_claim_ids": [
                "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
              ]
            },
            "reliability": {
              "score": 1,
              "completed_assignments": 0
            },
            "fatigue": {
              "score": 1,
              "hours_worked_last_7_days": 1
            },
            "cost": {
              "score": 1
            }
          },
          "total_score": 1,
          "rank": 1,
          "offered": true,
          "non_offer_reason": {
            "code": "ranked_below_threshold",
            "worker_message": "This job was filled before your turn came round."
          }
        }
      ],
      "escalated": true
    }
  ],
  "surge": {
    "mocked": true,
    "index": 1
  }
}
GET/v1/allocation/work_orders/{work_order_id}/offers#

List the offers made for a work order

allocation.offers.list

List every offer the cascade has made for this order, in the order it made them, with the round each belongs to.

A declined or expired offer keeps its reason, so you can read the cascade as a sequence rather than a final state.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Return only offers in one state: made while the worker can still answer, accepted, declined, expired once expires_at passed unanswered, or withdrawn when the order filled elsewhere, was cancelled, or the worker left the cascade. Leave it out to get every offer.

madeaccepteddeclinedexpiredwithdrawn

Returns

A page of offers.

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 Offer required

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

13 fields of Offer
id string required

The offer's identifier. It starts with match_geo_aware_workforce_orchestrator_offer_ and never changes; pass it as {offer_id} to GET /v1/allocation/offers/{offer_id} and to its /accept and /decline calls.

object always "offer" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this offer is for, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order and its ranking.

worker_id string required

The worker this offer went to, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. One offer names one worker.

status string required
madeaccepteddeclinedexpiredwithdrawn
round integer · minimum 1 required

The cascade round this offer belongs to.

rank integer · minimum 1 optional

Where the worker stood in the round's ranking when this offer was made, 1 being the top.

expires_at string · date-time required

An offer that is not answered by this time is treated as a decline.

closed_reason one of optional

Set where the offer ended without acceptance, including where the order filled elsewhere.

NonOfferReasonor
assignment_id string · nullable optional

The id of the assignment this offer became, starting with match_geo_aware_workforce_orchestrator_assignment_: the record POST /v1/allocation/offers/{offer_id}/accept returned. null until the offer is accepted.

made_at string · date-time optional

When the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), status (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), status (optional)
const result = await api.allocationOffersList({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit: 25, status: 'made' });
// query parameters: limit (optional), starting_after (optional), status (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional)
result = api.allocation_offers_list(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit=25, status='made')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional)
$result = $api->allocationOffersList(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit: 25, status: 'made');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.allocationOffersList("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 25, null, MatchOfferStatus.fromValue("made"));
// query parameters: limit (optional), starting_after (optional), status (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.AllocationOffersList(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", limit: 25, status: MatchOfferStatus.Made);
// query parameters: limit (optional), starting_after (optional), status (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made");
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), starting_after (optional), status (optional)
result, _, err := client.MATCHAPI.AllocationOffersList(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Limit(25).Status(droomwork.MatchOfferStatus("made")).Execute()
// query parameters: limit (optional), starting_after (optional), status (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offers?limit=25&status=made", 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": "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "offer",
      "livemode": true,
      "mocked": true,
      "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "status": "made",
      "round": 1,
      "expires_at": "2026-09-01T09:00:00Z",
      "rank": 1,
      "closed_reason": {
        "code": "ranked_below_threshold",
        "worker_message": "This job was filled before your turn came round."
      },
      "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "made_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
GET/v1/allocation/offers/{offer_id}#

Retrieve an offer

allocation.offers.retrieve

Read one offer, and where it did not happen, why.

Every eligible worker who did not receive this offer is given a reason from a fixed set, in plain language, and the worker can see it. The set is closed, so you can handle every value, and a worker gets the same wording every time.

Path parameters

offer_id string required

The offer's identifier, from the id of an offer listed at GET /v1/allocation/work_orders/{work_order_id}/offers or received on the offer.made event. It starts with match_geo_aware_workforce_orchestrator_offer_.

Returns

The offer.

id string required

The offer's identifier. It starts with match_geo_aware_workforce_orchestrator_offer_ and never changes; pass it as {offer_id} to GET /v1/allocation/offers/{offer_id} and to its /accept and /decline calls.

object always "offer" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this offer is for, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order and its ranking.

worker_id string required

The worker this offer went to, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. One offer names one worker.

status string required
madeaccepteddeclinedexpiredwithdrawn
round integer · minimum 1 required

The cascade round this offer belongs to.

rank integer · minimum 1 optional

Where the worker stood in the round's ranking when this offer was made, 1 being the top.

expires_at string · date-time required

An offer that is not answered by this time is treated as a decline.

closed_reason one of optional

Set where the offer ended without acceptance, including where the order filled elsewhere.

NonOfferReasonor
assignment_id string · nullable optional

The id of the assignment this offer became, starting with match_geo_aware_workforce_orchestrator_assignment_: the record POST /v1/allocation/offers/{offer_id}/accept returned. null until the offer is accepted.

made_at string · date-time optional

When the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.

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 GET "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationOffersRetrieve({ offerId: 'match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

result = api.allocation_offers_retrieve(offer_id='match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationOffersRetrieve(offer_id: 'match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;

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

var result = api.allocationOffersRetrieve("match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

var result = api.AllocationOffersRetrieve(offerId: "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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.MATCHAPI.AllocationOffersRetrieve(ctx, "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "offer",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "made",
  "round": 1,
  "expires_at": "2026-09-01T09:00:00Z",
  "rank": 1,
  "closed_reason": {
    "code": "ranked_below_threshold",
    "worker_message": "This job was filled before your turn came round."
  },
  "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "made_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/offers/{offer_id}/accept#

Accept an offer

allocation.offers.accept

Accept an offer and the work order is reserved and withdrawn from every other live cascade in the same operation. Two workers accepting the same order at the same moment can't both succeed.

The first acceptance returns the assignment. Every later one is refused with work_order_already_filled and status 409, and the offer that lost is closed with that reason.

Where the order has a headcount above one, it stays open until the headcount is met, and only the last acceptance closes it.

Path parameters

offer_id string required

The offer's identifier, from the id of an offer listed at GET /v1/allocation/work_orders/{work_order_id}/offers or received on the offer.made event. It starts with match_geo_aware_workforce_orchestrator_offer_.

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

accepted_at string · date-time required

When the worker accepted, as an RFC 3339 timestamp in UTC. An offer left unanswered past its expires_at has already been treated as a decline.

position GeoPoint optional

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

Returns

The assignment created by the acceptance.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"accepted_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0}}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationOffersAccept({
  offerId: 'match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchOfferAcceptRequest: {"acceptedAt":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracyMetres":0}},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "accepted_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    }
  }),
});
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.MATCHApi(client)

result = api.allocation_offers_accept(offer_id='match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"accepted_at": "2026-09-01T09:00:00Z", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"accepted_at": "2026-09-01T09:00:00Z", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationOffersAccept($idempotencyKey, json_decode('{"accepted_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0}}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept');
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 => '{"accepted_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0}}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationOffersAccept("match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept"))
    .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("""
        {
          "accepted_at": "2026-09-01T09:00:00Z",
          "position": {
            "latitude": -90,
            "longitude": -180,
            "accuracy_metres": 0
          }
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationOffersAccept(offerId: "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "accepted_at": "2026-09-01T09:00:00Z",
      "position": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      }
    }
    """, 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.MATCHAPI.AllocationOffersAccept(ctx, "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchOfferAcceptRequest(body).Execute()
body := strings.NewReader(`{
  "accepted_at": "2026-09-01T09:00:00Z",
  "position": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  }
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/accept", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/offers/{offer_id}/decline#

Decline an offer

allocation.offers.decline

Decline an offer and the order is re-ranked against supply as it is at that moment, not the pool the first ranking saw. People who have since become available are considered, and people who have since gone are not.

Where the pool is exhausted the search radius widens within the bounds published on the work order, and each widening is recorded on the ranking round, so you can read it.

A timeout is treated as a decline with the reason offer_expired.

Path parameters

offer_id string required

The offer's identifier, from the id of an offer listed at GET /v1/allocation/work_orders/{work_order_id}/offers or received on the offer.made event. It starts with match_geo_aware_workforce_orchestrator_offer_.

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

reason string required

Why the worker declined: too_far, unavailable, rate_too_low, not_qualified, no_reason_given or offer_expired (unanswered past expires_at). The offer keeps it, so you can read the cascade as a sequence later.

too_farunavailablerate_too_lownot_qualifiedno_reason_givenoffer_expired
detail string optional

Optional words behind the reason, such as what the worker said. The reason code is what's required; send this when a code alone would lose something.

Returns

The declined offer, carrying the round the re-rank opened.

id string required

The offer's identifier. It starts with match_geo_aware_workforce_orchestrator_offer_ and never changes; pass it as {offer_id} to GET /v1/allocation/offers/{offer_id} and to its /accept and /decline calls.

object always "offer" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this offer is for, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order and its ranking.

worker_id string required

The worker this offer went to, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. One offer names one worker.

status string required
madeaccepteddeclinedexpiredwithdrawn
round integer · minimum 1 required

The cascade round this offer belongs to.

rank integer · minimum 1 optional

Where the worker stood in the round's ranking when this offer was made, 1 being the top.

expires_at string · date-time required

An offer that is not answered by this time is treated as a decline.

closed_reason one of optional

Set where the offer ended without acceptance, including where the order filled elsewhere.

NonOfferReasonor
assignment_id string · nullable optional

The id of the assignment this offer became, starting with match_geo_aware_workforce_orchestrator_assignment_: the record POST /v1/allocation/offers/{offer_id}/accept returned. null until the offer is accepted.

made_at string · date-time optional

When the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.

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.
404No record with that identifier.
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/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"too_far","detail":"The payee has no verified destination, so this line cannot be paid."}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationOffersDecline({
  offerId: 'match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchOfferDeclineRequest: {"reason":"too_far","detail":"The payee has no verified destination, so this line cannot be paid."},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "too_far",
    "detail": "The payee has no verified destination, so this line cannot be paid."
  }),
});
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.MATCHApi(client)

result = api.allocation_offers_decline(offer_id='match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"reason": "too_far", "detail": "The payee has no verified destination, so this line cannot be paid."})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"reason": "too_far", "detail": "The payee has no verified destination, so this line cannot be paid."},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationOffersDecline($idempotencyKey, json_decode('{"reason":"too_far","detail":"The payee has no verified destination, so this line cannot be paid."}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline');
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 => '{"reason":"too_far","detail":"The payee has no verified destination, so this line cannot be paid."}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationOffersDecline("match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline"))
    .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("""
        {
          "reason": "too_far",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationOffersDecline(offerId: "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "reason": "too_far",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
    """, 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.MATCHAPI.AllocationOffersDecline(ctx, "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchOfferDeclineRequest(body).Execute()
body := strings.NewReader(`{
  "reason": "too_far",
  "detail": "The payee has no verified destination, so this line cannot be paid."
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/offers/match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/decline", 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": "match_geo_aware_workforce_orchestrator_offer_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "offer",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "made",
  "round": 1,
  "expires_at": "2026-09-01T09:00:00Z",
  "rank": 1,
  "closed_reason": {
    "code": "ranked_below_threshold",
    "worker_message": "This job was filled before your turn came round."
  },
  "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "made_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/dispersion#

Retrieve offer dispersion across the eligible population

allocation.dispersion.retrieve

Read the fairness floor now, not at the end of a period.

Eligible workers in the long tail are entitled to a defined minimum offer exposure. You get the exposure each band actually received, the floor in force, and the workers currently below it, so you can act on it today.

Query parameters

window string optional

The period the figures cover: last_24_hours, last_7_days or last_30_days. Leave it out for last_7_days.

last_24_hourslast_7_dayslast_30_days
occupation_code string optional

Restricts the report to one occupation, by its taxonomy code such as ng-7412, the occupation_code you set at POST /v1/allocation/work_orders: the offers made for that work and the workers eligible for it. Leave it out to get every occupation.

zone string optional

Restricts the report to one operational zone, by the zone's id such as zone_lagos_mainland_03, as zone.id reads on a supply state from GET /v1/allocation/supply_states. Leave it out to get every zone.

Returns

The dispersion report.

object always "dispersion_report" required

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

window string required

The period the figures cover: last_24_hours, last_7_days or last_30_days. The window you sent to GET /v1/allocation/dispersion, or last_7_days when you left it out.

last_24_hourslast_7_dayslast_30_days
floor_offers_per_window integer · minimum 0 required

The defined minimum offer exposure an eligible worker is entitled to.

bands array of DispersionBand required

One entry per band of the eligible population, from top_decile to long_tail: how many workers it holds, the offers they got, the mean per worker and how many fell below the floor.

5 fields of DispersionBand
band string required

Which slice of the eligible population this row is, from the most often offered to the least: top_decile, upper_quartile, median, lower_quartile or long_tail. The long tail is the band the floor protects.

top_decileupper_quartilemedianlower_quartilelong_tail
eligible_workers integer · minimum 0 required

How many eligible workers fall in this band in the window. Eligible means they passed every gate and could lawfully have been offered the work.

offers_made integer · minimum 0 required

How many offers went to workers in this band during the window, whatever each offer's outcome.

mean_offers_per_worker number optional

The average number of offers each eligible worker in this band received in the window. Read it against floor_offers_per_window: a band mean under the floor is a band to act on.

workers_below_floor integer · minimum 0 required

Eligible workers who did not reach the minimum exposure in this window.

workers_below_floor integer · minimum 0 required

How many eligible workers received fewer than floor_offers_per_window offers in this window, across every band. These are the people to act on today.

computed_at string · date-time optional

When these figures were taken, as an RFC 3339 timestamp in UTC. The floor is reported continuously, so ask again for a later reading.

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?
# query parameters: window (optional), occupation_code (optional), zone (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: window (optional), occupation_code (optional), zone (optional)
const result = await api.allocationDispersionRetrieve({ window: 'last_24_hours', occupationCode: 'ng-7412', zone: 'zone_lagos_mainland_03' });
// query parameters: window (optional), occupation_code (optional), zone (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03', {
  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.MATCHApi(client)

# query parameters: window (optional), occupation_code (optional), zone (optional)
result = api.allocation_dispersion_retrieve(window='last_24_hours', occupation_code='ng-7412', zone='zone_lagos_mainland_03')
import os

import requests

# query parameters: window (optional), occupation_code (optional), zone (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: window (optional), occupation_code (optional), zone (optional)
$result = $api->allocationDispersionRetrieve(window: 'last_24_hours', occupation_code: 'ng-7412', zone: 'zone_lagos_mainland_03');
<?php
// query parameters: window (optional), occupation_code (optional), zone (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03');
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.MatchApi;

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

// query parameters: window (optional), occupation_code (optional), zone (optional)
var result = api.allocationDispersionRetrieve("last_24_hours", "ng-7412", "zone_lagos_mainland_03");
// query parameters: window (optional), occupation_code (optional), zone (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03"))
    .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 MATCHApi(config);

// query parameters: window (optional), occupation_code (optional), zone (optional)
var result = api.AllocationDispersionRetrieve(window: "last_24_hours", occupationCode: "ng-7412", zone: "zone_lagos_mainland_03");
// query parameters: window (optional), occupation_code (optional), zone (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03");
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: window (optional), occupation_code (optional), zone (optional)
result, _, err := client.MATCHAPI.AllocationDispersionRetrieve(ctx).Window("last_24_hours").OccupationCode("ng-7412").Zone("zone_lagos_mainland_03").Execute()
// query parameters: window (optional), occupation_code (optional), zone (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/dispersion?window=last_24_hours&occupation_code=ng-7412&zone=zone_lagos_mainland_03", 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": "dispersion_report",
  "window": "last_24_hours",
  "floor_offers_per_window": 0,
  "bands": [
    {
      "band": "top_decile",
      "eligible_workers": 0,
      "offers_made": 0,
      "workers_below_floor": 0,
      "mean_offers_per_worker": 1
    }
  ],
  "workers_below_floor": 0,
  "computed_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/assignments#

List assignments

allocation.assignments.list

Lists your assignments, newest first.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Return only assignments in one state: accepted at creation, en_route while the worker travels, arrived, in_progress, completed, exception where the signals disagreed, or cancelled if never started. Leave it out to get every state.

accepteden_routearrivedin_progresscompletedexceptioncancelled
work_order_id string optional

Return only the assignments filling one work order, by its id from POST /v1/allocation/work_orders or GET /v1/allocation/work_orders, starting with match_geo_aware_workforce_orchestrator_work_order_. Leave it out to get every work order.

worker_id string optional

Return only one worker's assignments, by their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens, the same worker_id as on their supply state. Leave it out to get every worker.

Returns

A page of assignments.

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 Assignment required

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

17 fields of Assignment
id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
const result = await api.allocationAssignmentsList({ limit: 25, status: 'accepted', workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', workerId: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
result = api.allocation_assignments_list(limit=25, status='accepted', work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', worker_id='sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
$result = $api->allocationAssignmentsList(limit: 25, status: 'accepted', work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', worker_id: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
var result = api.allocationAssignmentsList(25, null, MatchAssignmentStatus.fromValue("accepted"), "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
var result = api.AllocationAssignmentsList(limit: 25, status: MatchAssignmentStatus.Accepted, workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", workerId: "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
result, _, err := client.MATCHAPI.AllocationAssignmentsList(ctx).Limit(25).Status(droomwork.MatchAssignmentStatus("accepted")).WorkOrderId("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").WorkerId("sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
// query parameters: limit (optional), starting_after (optional), status (optional), work_order_id (optional), worker_id (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/assignments?limit=25&status=accepted&work_order_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&worker_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "assignment",
      "livemode": true,
      "mocked": true,
      "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "status": "accepted",
      "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "geofence_entry": {
        "entered_at": "2026-09-01T09:00:00Z",
        "position": {
          "latitude": -90,
          "longitude": -180,
          "accuracy_metres": 0
        },
        "radius_metres": 0
      },
      "start_passcode_verified_at": "2026-09-01T09:00:00Z",
      "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
      "dwell": {
        "started_at": "2026-09-01T09:00:00Z",
        "ended_at": "2026-09-01T09:00:00Z",
        "seconds": 0
      },
      "telemetry_continuity": {
        "continuous": true,
        "gap_count": 0,
        "longest_gap_seconds": 0
      },
      "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "cancelled_reason": "example",
      "created_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
POST/v1/allocation/assignments#

Create an assignment

allocation.assignments.create

Assignments normally arise when an offer is accepted. Create one directly to bring work you agreed elsewhere under the same proof of service as the rest. That is what makes it payable.

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

work_order_id string optional

The work order this assignment fills, by its id from POST /v1/allocation/work_orders or GET /v1/allocation/work_orders, starting with match_geo_aware_workforce_orchestrator_work_order_.

worker_id string optional

The worker assigned, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens, the same worker_id as on their supply state at GET /v1/allocation/supply_states/{worker_id}.

Returns

The assignment.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
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/allocation/assignments" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"work_order_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","worker_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAssignmentsCreate({});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  }),
});
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.MATCHApi(client)

result = api.allocation_assignments_create()
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsCreate();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments');
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 => '{"work_order_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","worker_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationAssignmentsCreate();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments"))
    .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("""
        {
          "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsCreate();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
    """, 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.MATCHAPI.AllocationAssignmentsCreate(ctx).Execute()
body := strings.NewReader(`{
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/assignments/{assignment_id}#

Retrieve an assignment

allocation.assignments.retrieve

Read the assignment and every signal collected against it so far, including the ones that did not corroborate.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

Returns

The assignment.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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 GET "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAssignmentsRetrieve({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

result = api.allocation_assignments_retrieve(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsRetrieve(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;

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

var result = api.allocationAssignmentsRetrieve("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsRetrieve(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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.MATCHAPI.AllocationAssignmentsRetrieve(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/arrival#

Confirm arrival at the work location

allocation.assignments.confirm_arrival

Send the geofence entry and the start passcode together. Arrival needs both, because no single signal can establish that a worker was where the work is.

A request carrying only one of the two is refused with corroboration_insufficient. Nothing is recorded as a partial arrival, because a partial arrival would later read as an arrival.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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

geofence_entry GeofenceEntry required

One of the two signals arrival rests on.

3 fields of GeofenceEntry
entered_at string · date-time required

When the worker's position first fell inside the geofence, as an RFC 3339 timestamp in UTC.

position GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

radius_metres integer · minimum 0 optional

The geofence's radius around the work location, in whole metres: how near a reading had to be to count as inside.

start_passcode string · write only required

Held by the requester and given to the worker on site. Never returned.

Returns

The assignment, now arrived.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"geofence_entry":{"entered_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"radius_metres":0},"start_passcode":"example"}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAssignmentsConfirmArrival({
  assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchArrivalRequest: {"geofenceEntry":{"enteredAt":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracyMetres":0},"radiusMetres":0},"startPasscode":"example"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "geofence_entry": {
      "entered_at": "2026-09-01T09:00:00Z",
      "position": {
        "latitude": -90,
        "longitude": -180,
        "accuracy_metres": 0
      },
      "radius_metres": 0
    },
    "start_passcode": "example"
  }),
});
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.MATCHApi(client)

result = api.allocation_assignments_confirm_arrival(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"geofence_entry": {"entered_at": "2026-09-01T09:00:00Z", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "radius_metres": 0}, "start_passcode": "example"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"geofence_entry": {"entered_at": "2026-09-01T09:00:00Z", "position": {"latitude": -90, "longitude": -180, "accuracy_metres": 0}, "radius_metres": 0}, "start_passcode": "example"},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsConfirmArrival($idempotencyKey, json_decode('{"geofence_entry":{"entered_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"radius_metres":0},"start_passcode":"example"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival');
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 => '{"geofence_entry":{"entered_at":"2026-09-01T09:00:00Z","position":{"latitude":-90,"longitude":-180,"accuracy_metres":0},"radius_metres":0},"start_passcode":"example"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationAssignmentsConfirmArrival("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival"))
    .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("""
        {
          "geofence_entry": {
            "entered_at": "2026-09-01T09:00:00Z",
            "position": {
              "latitude": -90,
              "longitude": -180,
              "accuracy_metres": 0
            },
            "radius_metres": 0
          },
          "start_passcode": "example"
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsConfirmArrival(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "geofence_entry": {
        "entered_at": "2026-09-01T09:00:00Z",
        "position": {
          "latitude": -90,
          "longitude": -180,
          "accuracy_metres": 0
        },
        "radius_metres": 0
      },
      "start_passcode": "example"
    }
    """, 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.MATCHAPI.AllocationAssignmentsConfirmArrival(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchArrivalRequest(body).Execute()
body := strings.NewReader(`{
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode": "example"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrival", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/completion#

Confirm completion of the work

allocation.assignments.confirm_completion

Send the completion passcode, the dwell duration at the location and telemetry continuity across the working window. Completion needs all three, because no single one of them establishes that work occurred.

Where the three agree, the assignment completes and the proof of service bundle is sealed. Where they disagree, the assignment moves to exception for a person to adjudicate and does not settle. You get a 200 with an assignment in exception, not an error: your submission was accepted, and its outcome is a state.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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

completion_passcode string · write only required

Never returned on any response.

dwell DwellRecord required

How long the worker was at the location. One of the three signals completion rests on.

3 fields of DwellRecord
started_at string · date-time required

When the worker's time at the location began, as an RFC 3339 timestamp in UTC.

ended_at string · date-time required

When the worker's time at the location ended, as an RFC 3339 timestamp in UTC.

seconds integer · minimum 0 required

The whole seconds between started_at and ended_at: the dwell duration completion is judged on. Never negative.

telemetry_continuity TelemetryContinuity required

Whether the heartbeat stream held across the working window. A long gap does not disprove the work, and it does mean the completion needs a person rather than a rule.

3 fields of TelemetryContinuity
continuous boolean required

true when the heartbeat stream held across the whole working window, false when it broke. false doesn't disprove the work; gap_count and longest_gap_seconds say how badly it broke.

gap_count integer · minimum 0 required

How many times the heartbeat stream broke during the working window. 0 when it was continuous.

longest_gap_seconds integer · minimum 0 required

The longest single break in the heartbeat stream during the working window, in whole seconds, or 0 when there was none. A long gap doesn't disprove the work; it sends the completion to a person.

notes string optional

Free text to send with the completion, such as what the worker reported on site. It is not a signal: the three signals decide, and a note never stands in for one.

Returns

The assignment, either completed with a sealed proof or moved to exception where the signals disagreed.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"completion_passcode":"example","dwell":{"started_at":"2026-09-01T09:00:00Z","ended_at":"2026-09-01T09:00:00Z","seconds":0},"telemetry_continuity":{"continuous":true,"gap_count":0,"longest_gap_seconds":0},"notes":"example"}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAssignmentsConfirmCompletion({
  assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchCompletionRequest: {"completionPasscode":"example","dwell":{"startedAt":"2026-09-01T09:00:00Z","endedAt":"2026-09-01T09:00:00Z","seconds":0},"telemetryContinuity":{"continuous":true,"gapCount":0,"longestGapSeconds":0},"notes":"example"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "completion_passcode": "example",
    "dwell": {
      "started_at": "2026-09-01T09:00:00Z",
      "ended_at": "2026-09-01T09:00:00Z",
      "seconds": 0
    },
    "telemetry_continuity": {
      "continuous": true,
      "gap_count": 0,
      "longest_gap_seconds": 0
    },
    "notes": "example"
  }),
});
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.MATCHApi(client)

result = api.allocation_assignments_confirm_completion(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"completion_passcode": "example", "dwell": {"started_at": "2026-09-01T09:00:00Z", "ended_at": "2026-09-01T09:00:00Z", "seconds": 0}, "telemetry_continuity": {"continuous": True, "gap_count": 0, "longest_gap_seconds": 0}, "notes": "example"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"completion_passcode": "example", "dwell": {"started_at": "2026-09-01T09:00:00Z", "ended_at": "2026-09-01T09:00:00Z", "seconds": 0}, "telemetry_continuity": {"continuous": True, "gap_count": 0, "longest_gap_seconds": 0}, "notes": "example"},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsConfirmCompletion($idempotencyKey, json_decode('{"completion_passcode":"example","dwell":{"started_at":"2026-09-01T09:00:00Z","ended_at":"2026-09-01T09:00:00Z","seconds":0},"telemetry_continuity":{"continuous":true,"gap_count":0,"longest_gap_seconds":0},"notes":"example"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion');
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 => '{"completion_passcode":"example","dwell":{"started_at":"2026-09-01T09:00:00Z","ended_at":"2026-09-01T09:00:00Z","seconds":0},"telemetry_continuity":{"continuous":true,"gap_count":0,"longest_gap_seconds":0},"notes":"example"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationAssignmentsConfirmCompletion("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion"))
    .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("""
        {
          "completion_passcode": "example",
          "dwell": {
            "started_at": "2026-09-01T09:00:00Z",
            "ended_at": "2026-09-01T09:00:00Z",
            "seconds": 0
          },
          "telemetry_continuity": {
            "continuous": true,
            "gap_count": 0,
            "longest_gap_seconds": 0
          },
          "notes": "example"
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsConfirmCompletion(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "completion_passcode": "example",
      "dwell": {
        "started_at": "2026-09-01T09:00:00Z",
        "ended_at": "2026-09-01T09:00:00Z",
        "seconds": 0
      },
      "telemetry_continuity": {
        "continuous": true,
        "gap_count": 0,
        "longest_gap_seconds": 0
      },
      "notes": "example"
    }
    """, 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.MATCHAPI.AllocationAssignmentsConfirmCompletion(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchCompletionRequest(body).Execute()
body := strings.NewReader(`{
  "completion_passcode": "example",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "notes": "example"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/completion", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/cancel#

Cancel an assignment

allocation.assignments.cancel

Cancel an assignment that has not started.

A revocation from ANCHOR or a lapse of legal standing from RAIL cancels the same way: the worker is removed from every live cascade and any unstarted assignment is cancelled within 60 seconds, with the reason worker_removed_from_cascade.

An assignment already in progress can't be cancelled. Work that has started resolves through completion or through exception, so the evidence of what happened survives.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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

reason string required

Why you are cancelling: requester_withdrew, no_longer_required, raised_in_error or worker_removed_from_cascade (the worker lost standing in ANCHOR or RAIL). It reaches the worker where one was offered or assigned.

requester_withdrewno_longer_requiredraised_in_errorworker_removed_from_cascade
detail string optional

More about why, in your own words, for whoever reads the record later. Optional; reason is the fixed code.

Returns

The cancelled assignment.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
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/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAssignmentsCancel({
  assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchCancellationRequest: {"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "requester_withdrew",
    "detail": "The payee has no verified destination, so this line cannot be paid."
  }),
});
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.MATCHApi(client)

result = api.allocation_assignments_cancel(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"reason": "requester_withdrew", "detail": "The payee has no verified destination, so this line cannot be paid."})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"reason": "requester_withdrew", "detail": "The payee has no verified destination, so this line cannot be paid."},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsCancel($idempotencyKey, json_decode('{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel');
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 => '{"reason":"requester_withdrew","detail":"The payee has no verified destination, so this line cannot be paid."}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationAssignmentsCancel("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel"))
    .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("""
        {
          "reason": "requester_withdrew",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsCancel(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "reason": "requester_withdrew",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
    """, 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.MATCHAPI.AllocationAssignmentsCancel(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchCancellationRequest(body).Execute()
body := strings.NewReader(`{
  "reason": "requester_withdrew",
  "detail": "The payee has no verified destination, so this line cannot be paid."
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/cancel", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/assignments/{assignment_id}/proof#

Retrieve the proof of service for an assignment

allocation.proofs.retrieve

Read the sealed bundle: every signal that established the work happened, the seal over them, and the settlement event sent to Droomwork Pay.

The bundle carries evidence and never an amount. MATCH computes no money and moves none. Droomwork Pay settles against what is sealed here.

The seal covers the bundle as sealed. Anything added afterwards is a new bundle, so a sealed proof can't be edited into agreement with a later claim.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

Returns

The sealed proof of service.

id string required

The proof's identifier. It starts with match_geo_aware_workforce_orchestrator_proof_ and never changes; it matches proof_of_service_id on the assignment, so you can tie a sealed bundle back to the work it proves.

object always "proof_of_service" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

assignment_id string required

The id of the assignment this proof was sealed for, starting with match_geo_aware_workforce_orchestrator_assignment_. Pass it to GET /v1/allocation/assignments/{assignment_id} to read the signals as they were collected.

work_order_id string optional

The work order the assignment filled, by its id from POST /v1/allocation/work_orders, starting match_geo_aware_workforce_orchestrator_work_order_. Use it to match the proof to the order when you reconcile what settled, without reading the assignment.

worker_id string optional

The worker the proof is for, as the sub_ subject reference you chose for them and first sent as subject_ref at POST /v1/identity/consent_tokens. It is the worker_id on the assignment this proof was sealed for.

signals array of string required

Never fewer than two. One signal cannot establish that work occurred, so a bundle resting on one is not a bundle.

established_by string optional

How the proof was established: signals_agreed when the collected signals agreed on their own, adjudication when a person upheld the completion at POST /v1/allocation/exceptions/{exception_id}/adjudicate.

signals_agreedadjudication
seal Seal required

The cryptographic seal over the bundle as it stood. Anything added afterwards is a new bundle, so a sealed proof cannot be edited into agreement with a later claim.

4 fields of Seal
algorithm string required

The algorithm behind digest, such as sha256. Keep it with digest and key_reference: together they say exactly how this bundle was sealed.

digest string required

The seal itself: the digest over the bundle as sealed, prefixed with its algorithm, such as sha256:9f2c1e0043a1b8. Any change to the bundle gives a different digest.

sealed_at string · date-time required

When the bundle was sealed, as an RFC 3339 timestamp in UTC. Nothing added after this moment is inside the seal.

key_reference string optional

Which signing key sealed it, so a seal stays verifiable across a key rotation.

settlement_event_id string optional

The id of the proof_of_service.sealed event that carried this proof to Droomwork Pay, the event settlement runs from. It starts with evt_; read it at GET /v1/allocation/events/{event_id}.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

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

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

const result = await api.allocationProofsRetrieve({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof', {
  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.MATCHApi(client)

result = api.allocation_proofs_retrieve(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationProofsRetrieve(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof');
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.MatchApi;

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

var result = api.allocationProofsRetrieve("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof"))
    .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 MATCHApi(config);

var result = api.AllocationProofsRetrieve(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof");
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.MATCHAPI.AllocationProofsRetrieve(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/proof", 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": "match_geo_aware_workforce_orchestrator_proof_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "proof_of_service",
  "livemode": true,
  "mocked": true,
  "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "signals": [
    "geofence_entry",
    "geofence_entry"
  ],
  "seal": {
    "algorithm": "sha256",
    "digest": "sha256:9f2c1e0043a1b8",
    "sealed_at": "2026-09-01T09:00:00Z",
    "key_reference": "paye-2026-09-rivers"
  },
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "established_by": "signals_agreed",
  "settlement_event_id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
GET/v1/allocation/proofs#

List sealed proofs of service

allocation.proofs.list

List every sealed bundle for your organisation, newest first. Use it to reconcile what has settled against what was sealed.

No bundle carries an amount.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

assignment_id string optional

Return only the proof sealed for one assignment, by its id from POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, starting match_geo_aware_workforce_orchestrator_assignment_. Leave it out to get every proof.

sealed_after string optional

Return only proofs sealed after this moment, as an RFC 3339 timestamp in UTC, matched against seal.sealed_at. Use it to pick up what was sealed since your last reconciliation; leave it out to get every proof.

Returns

A page of sealed proofs.

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 ProofOfService required

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

11 fields of ProofOfService
id string required

The proof's identifier. It starts with match_geo_aware_workforce_orchestrator_proof_ and never changes; it matches proof_of_service_id on the assignment, so you can tie a sealed bundle back to the work it proves.

object always "proof_of_service" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

assignment_id string required

The id of the assignment this proof was sealed for, starting with match_geo_aware_workforce_orchestrator_assignment_. Pass it to GET /v1/allocation/assignments/{assignment_id} to read the signals as they were collected.

work_order_id string optional

The work order the assignment filled, by its id from POST /v1/allocation/work_orders, starting match_geo_aware_workforce_orchestrator_work_order_. Use it to match the proof to the order when you reconcile what settled, without reading the assignment.

worker_id string optional

The worker the proof is for, as the sub_ subject reference you chose for them and first sent as subject_ref at POST /v1/identity/consent_tokens. It is the worker_id on the assignment this proof was sealed for.

signals array of string required

Never fewer than two. One signal cannot establish that work occurred, so a bundle resting on one is not a bundle.

established_by string optional

How the proof was established: signals_agreed when the collected signals agreed on their own, adjudication when a person upheld the completion at POST /v1/allocation/exceptions/{exception_id}/adjudicate.

signals_agreedadjudication
seal Seal required

The cryptographic seal over the bundle as it stood. Anything added afterwards is a new bundle, so a sealed proof cannot be edited into agreement with a later claim.

4 fields of Seal
algorithm string required

The algorithm behind digest, such as sha256. Keep it with digest and key_reference: together they say exactly how this bundle was sealed.

digest string required

The seal itself: the digest over the bundle as sealed, prefixed with its algorithm, such as sha256:9f2c1e0043a1b8. Any change to the bundle gives a different digest.

sealed_at string · date-time required

When the bundle was sealed, as an RFC 3339 timestamp in UTC. Nothing added after this moment is inside the seal.

key_reference string optional

Which signing key sealed it, so a seal stays verifiable across a key rotation.

settlement_event_id string optional

The id of the proof_of_service.sealed event that carried this proof to Droomwork Pay, the event settlement runs from. It starts with evt_; read it at GET /v1/allocation/events/{event_id}.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
const result = await api.allocationProofsList({ limit: 25, assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', sealedAfter: '2026-01-01T00:00:00Z' });
// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
result = api.allocation_proofs_list(limit=25, assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', sealed_after='2026-01-01T00:00:00Z')
import os

import requests

# query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
$result = $api->allocationProofsList(limit: 25, assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', sealed_after: '2026-01-01T00:00:00Z');
<?php
// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z');
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.MatchApi;

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

// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
var result = api.allocationProofsList(25, null, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "2026-01-01T00:00:00Z");
// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
var result = api.AllocationProofsList(limit: 25, assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", sealedAfter: "2026-01-01T00:00:00Z");
// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z");
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), starting_after (optional), assignment_id (optional), sealed_after (optional)
result, _, err := client.MATCHAPI.AllocationProofsList(ctx).Limit(25).AssignmentId("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").SealedAfter("2026-01-01T00:00:00Z").Execute()
// query parameters: limit (optional), starting_after (optional), assignment_id (optional), sealed_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/proofs?limit=25&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&sealed_after=2026-01-01T00%3A00%3A00Z", 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": "match_geo_aware_workforce_orchestrator_proof_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "proof_of_service",
      "livemode": true,
      "mocked": true,
      "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "signals": [
        "geofence_entry",
        "geofence_entry"
      ],
      "seal": {
        "algorithm": "sha256",
        "digest": "sha256:9f2c1e0043a1b8",
        "sealed_at": "2026-09-01T09:00:00Z",
        "key_reference": "paye-2026-09-rivers"
      },
      "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "established_by": "signals_agreed",
      "settlement_event_id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
GET/v1/allocation/exceptions#

List assignment exceptions

allocation.exceptions.list

List completions whose signals did not agree and are waiting for a person.

An unverifiable completion lands here instead of settling. Nothing settles while the evidence is in conflict, and the conflict is stated for you rather than resolved by a default.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Return only exceptions in one state: open while a person has yet to decide, upheld once the completion was accepted and the proof sealed, rejected once it was not and the assignment closed without a proof. Leave it out to get every state.

openupheldrejected
assignment_id string optional

Return only the exceptions raised on one assignment, by its id from POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, starting match_geo_aware_workforce_orchestrator_assignment_. Leave it out to get every assignment.

Returns

A page of exceptions.

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 AssignmentException required

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

9 fields of AssignmentException
id string required

The exception's identifier. It starts with match_geo_aware_workforce_orchestrator_exception_ and never changes; pass it as exception_id at POST /v1/allocation/exceptions/{exception_id}/adjudicate, or as starting_after to page past it.

object always "assignment_exception" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

assignment_id string required

The assignment whose completion is in question, by its id from POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, prefix match_geo_aware_workforce_orchestrator_assignment_. Nothing settles against it while open.

status string required
openupheldrejected
conflicting_signals array of object required

Which signals disagreed, named rather than summarised as a failure.

2 fields
signal string required

Which signal disagreed: geofence_entry or start_passcode for arrival; completion_passcode, dwell_duration or telemetry_continuity for completion. Name it in accepted_signals at POST /v1/allocation/exceptions/{exception_id}/adjudicate.

geofence_entrystart_passcodecompletion_passcodedwell_durationtelemetry_continuity
detail string required

What this signal said, in plain words, and where it parted from the others. Read every one before you decide: the conflict is stated for you, never resolved by a default.

decision one of optional

The ruling once a person has made one: the outcome, who decided, when, their reason and the signals they accepted, or null while the exception is open. It's never edited; a later disagreement is a new decision.

Decisionor
raised_at string · date-time optional

When the exception was raised, as an RFC 3339 timestamp in UTC. Nothing has settled against the assignment since this moment, and nothing will until a person decides.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
const result = await api.allocationExceptionsList({ limit: 25, status: 'open', assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
result = api.allocation_exceptions_list(limit=25, status='open', assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
$result = $api->allocationExceptionsList(limit: 25, status: 'open', assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
var result = api.allocationExceptionsList(25, null, MatchExceptionStatus.fromValue("open"), "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
var result = api.AllocationExceptionsList(limit: 25, status: MatchExceptionStatus.Open, assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
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), starting_after (optional), status (optional), assignment_id (optional)
result, _, err := client.MATCHAPI.AllocationExceptionsList(ctx).Limit(25).Status(droomwork.MatchExceptionStatus("open")).AssignmentId("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
// query parameters: limit (optional), starting_after (optional), status (optional), assignment_id (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/exceptions?limit=25&status=open&assignment_id=match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 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": "match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "assignment_exception",
      "livemode": true,
      "mocked": true,
      "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "status": "open",
      "conflicting_signals": [
        {
          "signal": "geofence_entry",
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
      ],
      "decision": {
        "outcome": "upheld",
        "decided_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
        "decided_at": "2026-09-01T09:00:00Z",
        "reason": "The requester confirmed the work in person.",
        "accepted_signals": [
          "example"
        ]
      },
      "raised_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
POST/v1/allocation/exceptions/{exception_id}/adjudicate#

Adjudicate an assignment exception

allocation.exceptions.adjudicate

Decide whether the work happened.

Uphold the completion and the proof of service is sealed from the signals that were collected, marking which of them you accepted. Reject it and the assignment closes without a proof, and nothing settles.

The decision, who made it and the reason are recorded and can't be edited. If you later disagree, make a new decision; this one stays as it is.

Path parameters

exception_id string required

The exception's identifier, from the id of an exception you listed at GET /v1/allocation/exceptions or received on the assignment.exception_raised webhook. It starts with match_geo_aware_workforce_orchestrator_exception_.

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

outcome string required

Your decision: upheld or dismissed. On a flag, upheld suspends the worker where more than one signal fired and dismissed returns them; on an exception, upheld seals the proof and dismissed closes the assignment without one.

uphelddismissed
reason string required

Why you decided as you did, in your own words, at least one character. It is recorded with the decision and can't be edited afterwards.

accepted_signals array of string optional

Name the signals you accepted, when only some persuaded you: by source from a flag's signals, or by signal from an exception's conflicting_signals. Recorded with the decision.

Returns

The adjudicated exception.

id string required

The exception's identifier. It starts with match_geo_aware_workforce_orchestrator_exception_ and never changes; pass it as exception_id at POST /v1/allocation/exceptions/{exception_id}/adjudicate, or as starting_after to page past it.

object always "assignment_exception" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

assignment_id string required

The assignment whose completion is in question, by its id from POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, prefix match_geo_aware_workforce_orchestrator_assignment_. Nothing settles against it while open.

status string required
openupheldrejected
conflicting_signals array of object required

Which signals disagreed, named rather than summarised as a failure.

2 fields
signal string required

Which signal disagreed: geofence_entry or start_passcode for arrival; completion_passcode, dwell_duration or telemetry_continuity for completion. Name it in accepted_signals at POST /v1/allocation/exceptions/{exception_id}/adjudicate.

geofence_entrystart_passcodecompletion_passcodedwell_durationtelemetry_continuity
detail string required

What this signal said, in plain words, and where it parted from the others. Read every one before you decide: the conflict is stated for you, never resolved by a default.

decision one of optional

The ruling once a person has made one: the outcome, who decided, when, their reason and the signals they accepted, or null while the exception is open. It's never edited; a later disagreement is a new decision.

Decisionor
raised_at string · date-time optional

When the exception was raised, as an RFC 3339 timestamp in UTC. Nothing has settled against the assignment since this moment, and nothing will until a person decides.

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.
404No record with that identifier.
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/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}'
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationExceptionsAdjudicate({
  exceptionId: 'match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
  idempotencyKey: crypto.randomUUID(),
  matchAdjudicateRequest: {"outcome":"upheld","reason":"The requester confirmed the work in person.","acceptedSignals":["example"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "outcome": "upheld",
    "reason": "The requester confirmed the work in person.",
    "accepted_signals": [
      "example"
    ]
  }),
});
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.MATCHApi(client)

result = api.allocation_exceptions_adjudicate(exception_id='match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"outcome": "upheld", "reason": "The requester confirmed the work in person.", "accepted_signals": ["example"]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"outcome": "upheld", "reason": "The requester confirmed the work in person.", "accepted_signals": ["example"]},
)
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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationExceptionsAdjudicate($idempotencyKey, json_decode('{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate');
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 => '{"outcome":"upheld","reason":"The requester confirmed the work in person.","accepted_signals":["example"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.MatchApi;

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

var result = api.allocationExceptionsAdjudicate("match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate"))
    .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("""
        {
          "outcome": "upheld",
          "reason": "The requester confirmed the work in person.",
          "accepted_signals": [
            "example"
          ]
        }
        """))
    .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 MATCHApi(config);

var result = api.AllocationExceptionsAdjudicate(exceptionId: "match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "outcome": "upheld",
      "reason": "The requester confirmed the work in person.",
      "accepted_signals": [
        "example"
      ]
    }
    """, 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.MATCHAPI.AllocationExceptionsAdjudicate(ctx, "match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).MatchAdjudicateRequest(body).Execute()
body := strings.NewReader(`{
  "outcome": "upheld",
  "reason": "The requester confirmed the work in person.",
  "accepted_signals": [
    "example"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/exceptions/match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/adjudicate", 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": "match_geo_aware_workforce_orchestrator_exception_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment_exception",
  "livemode": true,
  "mocked": true,
  "assignment_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "open",
  "conflicting_signals": [
    {
      "signal": "geofence_entry",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "decision": {
    "outcome": "upheld",
    "decided_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "decided_at": "2026-09-01T09:00:00Z",
    "reason": "The requester confirmed the work in person.",
    "accepted_signals": [
      "example"
    ]
  },
  "raised_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/supply_gaps#

List supply gaps

allocation.supply_gaps.list

List orders that went unfilled, each with the specific reason.

A cascade that exhausts the eligible pool, or an order that expires, is declared here rather than going quiet. Each gap is sent to FLOW as sourcing demand, so an unfilled order becomes a request to find people rather than a number in a report.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

reason string optional

Return only gaps declared for one reason: cascade_exhausted (the eligible pool ran out), expired (the order's expires_at passed), no_eligible_supply (nobody passed the gates) or all_offers_declined. Leave it out to get every reason.

cascade_exhaustedexpiredno_eligible_supplyall_offers_declined
occupation_code string optional

Return only gaps in one occupation, by the occupation_code you sent at POST /v1/allocation/work_orders, such as ng-7412. Leave it out to get gaps in every occupation.

zone string optional

Return only gaps whose order fell in one operational zone, by the zone's id as it reads in zone.id on each gap or on a receipt from POST /v1/allocation/heartbeats, such as zone_lagos_mainland_03. Leave it out to get gaps in every zone.

Returns

A page of supply gaps.

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 SupplyGap required

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

9 fields of SupplyGap
object always "supply_gap" required

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

work_order_id string required

The unfilled order, by its id from POST /v1/allocation/work_orders, starting match_geo_aware_workforce_orchestrator_work_order_. Read it at GET /v1/allocation/work_orders/{work_order_id} for the occupation, location, window and constraints.

reason string required

Why nobody took the work. Specific, because a gap without a reason cannot be acted on.

cascade_exhaustedexpiredno_eligible_supplyall_offers_declined
occupation_code string required

The occupation the order asked for, as its taxonomy code such as ng-7412. Filter the list on it to see where one trade keeps going unfilled.

zone one of optional

The operational zone the order's location falls in, with its id and name, or null when it falls in none. Pass the id as zone to filter the list to it.

Zoneor
eligible_considered integer · minimum 0 optional

How many workers the gates let through before the cascade ran out.

offers_made integer · minimum 0 optional

How many offers went out before the gap was declared, from 0 up. Read it beside eligible_considered: many eligible and few offers is a different problem from nobody eligible at all.

emitted_to_flow boolean optional

Whether the sourcing demand event was accepted by FLOW.

declared_at string · date-time required

When the order was declared unfilled, as an RFC 3339 timestamp in UTC: the moment the cascade ran out or the order expired, not the end of a reporting period.

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.

next_cursor string · nullable optional

Where the next page starts, or null when this is the last page. Pass it back unchanged to continue.

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?
# query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
const result = await api.allocationSupplyGapsList({ limit: 25, reason: 'cascade_exhausted', occupationCode: 'ng-7412', zone: 'zone_lagos_mainland_03' });
// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03', {
  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.MATCHApi(client)

# query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
result = api.allocation_supply_gaps_list(limit=25, reason='cascade_exhausted', occupation_code='ng-7412', zone='zone_lagos_mainland_03')
import os

import requests

# query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
$result = $api->allocationSupplyGapsList(limit: 25, reason: 'cascade_exhausted', occupation_code: 'ng-7412', zone: 'zone_lagos_mainland_03');
<?php
// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03');
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.MatchApi;
import com.droomwork.sdk.model.*;

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

// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
var result = api.allocationSupplyGapsList(25, null, MatchSupplyGapReason.fromValue("cascade_exhausted"), "ng-7412", "zone_lagos_mainland_03");
// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03"))
    .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 MATCHApi(config);

// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
var result = api.AllocationSupplyGapsList(limit: 25, reason: MatchSupplyGapReason.CascadeExhausted, occupationCode: "ng-7412", zone: "zone_lagos_mainland_03");
// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03");
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), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
result, _, err := client.MATCHAPI.AllocationSupplyGapsList(ctx).Limit(25).Reason(droomwork.MatchSupplyGapReason("cascade_exhausted")).OccupationCode("ng-7412").Zone("zone_lagos_mainland_03").Execute()
// query parameters: limit (optional), starting_after (optional), reason (optional), occupation_code (optional), zone (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/supply_gaps?limit=25&reason=cascade_exhausted&occupation_code=ng-7412&zone=zone_lagos_mainland_03", 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": [
    {
      "object": "supply_gap",
      "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "reason": "cascade_exhausted",
      "occupation_code": "ng-7412",
      "declared_at": "2026-09-01T09:00:00Z",
      "zone": {
        "id": "zone_lagos_mainland_03",
        "name": "Lagos Mainland 03"
      },
      "eligible_considered": 0,
      "offers_made": 0,
      "emitted_to_flow": true
    }
  ],
  "has_more": true,
  "next_cursor": "example"
}
GET/v1/allocation/readiness#

What MATCH needs, what you have, and what is missing

allocation.readiness.retrieve

Before anybody can be offered work, MATCH needs an anchored worker with a live Passport and an executed engagement, and before an order can open it needs a verified requester. This tells you, for each fact, whether it comes from the module that owns it or from you, and what is missing.

The gates listed here are the same gates the ranking applies. Nothing is checked at ranking time that is not reported here.

Returns

The readiness report.

object always "readiness_report" required

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

module string required

The module whose readiness endpoint you called, such as GET /v1/payroll/readiness: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance) or route (payouts).

anchorproofrailflowmatchrunremitroute
mode string required

integrated means a required fact comes from the Droomwork module that owns it; standalone means you supply it yourself under an attestation. The checks are the same in both, and what each fact is worth is recorded on its row as held.

integratedstandalone
ready boolean required

true when every row is satisfied and the module has what it needs from you. false when a required fact is missing or held at too low an assurance; rows says which.

rows array of ReadinessRow required

One row per fact the module requires: who owns it, the assurance it needs, what you hold and whether that satisfies it. The rows that are not satisfied are what to bring.

8 fields of ReadinessRow
fact string required

Named for what it is, not for who supplies it.

owner string required

The sibling module that owns this fact when running integrated.

contract_row string optional
required string required

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
held one of required
Assuranceor
source string · nullable optional
integratedstandalonenull
satisfied boolean required
missing_because string · nullable optional
not_suppliedassurance_too_lownull
always_enforced array of string optional

Gates that hold in every mode and cannot be attested away. Consent before any source is queried, bank account validation, the tax identifiers returns are filed under, and duplicate detection within the organisation's own population.

weight_set_in_force string · nullable optional

No ranking runs without one.

gates_applied array of EligibilityGate optional

The gates the ranking applies, listed here so that nothing is checked at ranking time that was not reported first.

workers_without_executed_engagement integer optional

They can hold availability and will never receive an offer.

workers_without_live_passport integer optional

How many of your workers have no live, unrevoked Passport from ANCHOR. They never enter a ranking and never receive an offer, whatever availability they set.

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/allocation/readiness" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationReadinessRetrieve({});
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/readiness', {
  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.MATCHApi(client)

result = api.allocation_readiness_retrieve()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/readiness',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationReadinessRetrieve();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/readiness');
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.MatchApi;

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

var result = api.allocationReadinessRetrieve();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/readiness"))
    .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 MATCHApi(config);

var result = api.AllocationReadinessRetrieve();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/readiness");
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.MATCHAPI.AllocationReadinessRetrieve(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/readiness", 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": "readiness_report",
  "module": "anchor",
  "mode": "integrated",
  "ready": true,
  "rows": [
    {
      "fact": "anchored_subject",
      "owner": "anchor",
      "required": "attested",
      "held": "attested",
      "satisfied": true,
      "contract_row": "6.1.13",
      "source": "integrated",
      "missing_because": "not_supplied"
    }
  ],
  "always_enforced": [
    "example"
  ],
  "weight_set_in_force": "example",
  "gates_applied": [
    "passport_live"
  ],
  "workers_without_executed_engagement": 1,
  "workers_without_live_passport": 1
}
POST/v1/allocation/assignments/{assignment_id}/start#

Start an assignment

allocation.assignments.start

Record that the worker began. It's recorded from the worker's side and checked against the geofence: a start nobody can corroborate is a start nobody will pay for.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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 assignment in its new state.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationAssignmentsStart({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start', {
  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.MATCHApi(client)

result = api.allocation_assignments_start(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsStart(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start');
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.MatchApi;

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

var result = api.allocationAssignmentsStart("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsStart(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start");
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.MATCHAPI.AllocationAssignmentsStart(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/start", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/arrive#

Record arrival at the site

allocation.assignments.arrive

Record a geofence entry, with its accuracy. Arrival is separate from starting: a worker can be on site and not yet working, and that difference is what a dispute turns on.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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 assignment in its new state.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationAssignmentsArrive({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive', {
  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.MATCHApi(client)

result = api.allocation_assignments_arrive(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsArrive(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive');
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.MatchApi;

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

var result = api.allocationAssignmentsArrive("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsArrive(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive");
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.MATCHAPI.AllocationAssignmentsArrive(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/arrive", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/depart#

Record departure from the site

allocation.assignments.depart

Close the dwell. Departure and completion are separate: leaving the site is not the same as finishing the work, and treating them as one is how unfinished work gets paid.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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 assignment in its new state.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationAssignmentsDepart({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart', {
  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.MATCHApi(client)

result = api.allocation_assignments_depart(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsDepart(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart');
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.MatchApi;

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

var result = api.allocationAssignmentsDepart("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsDepart(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart");
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.MATCHAPI.AllocationAssignmentsDepart(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/depart", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/complete#

Complete an assignment

allocation.assignments.complete

Record that the work is done. The completion passcode is checked here. A completion that can't be corroborated raises an exception rather than settling.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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 assignment in its new state.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationAssignmentsComplete({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete', {
  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.MATCHApi(client)

result = api.allocation_assignments_complete(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsComplete(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete');
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.MatchApi;

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

var result = api.allocationAssignmentsComplete("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsComplete(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete");
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.MATCHAPI.AllocationAssignmentsComplete(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/complete", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/assignments/{assignment_id}/raise_exception#

Raise an exception on an assignment

allocation.assignments.raise_exception

Send an unverifiable completion to a person rather than letting it settle. The conflicting signals are named rather than summarised as a failure, so whoever adjudicates sees every signal there was.

Path parameters

assignment_id string required

The assignment's identifier: the id returned by POST /v1/allocation/assignments or POST /v1/allocation/offers/{offer_id}/accept, or listed at GET /v1/allocation/assignments. It starts with match_geo_aware_workforce_orchestrator_assignment_.

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 assignment in its new state.

id string required

The assignment's identifier. It starts with match_geo_aware_workforce_orchestrator_assignment_ and never changes; pass it as {assignment_id} to GET /v1/allocation/assignments/{assignment_id} and its arrival, completion, cancel and proof calls.

object always "assignment" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

work_order_id string required

The id of the work order this assignment fills, as POST /v1/allocation/work_orders returned it, starting with match_geo_aware_workforce_orchestrator_work_order_. Pass it as {work_order_id} to read the order.

worker_id string required

The worker doing the work, as their sub_ subject reference: the subject_ref you chose for them at POST /v1/identity/consent_tokens. Every arrival and completion signal on this record is about this one person.

offer_id string optional

The id of the accepted offer this assignment came from, as listed at GET /v1/allocation/work_orders/{work_order_id}/offers, starting with match_geo_aware_workforce_orchestrator_offer_. Absent when you created it directly for work agreed elsewhere.

status string required

Work that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.

accepteden_routearrivedin_progresscompletedexceptioncancelled
geofence_entry one of optional

The worker's entry into the geofence around the work location: when, where and the radius, one of the two signals arrival rests on. Null until arrival is confirmed.

GeofenceEntryor
start_passcode_verified_at string · date-time · nullable optional

When the start passcode the requester gave the worker on site was verified, as an RFC 3339 timestamp in UTC: the second of the two arrival signals. Null until arrival is confirmed.

completion_passcode_verified_at string · date-time · nullable optional

When the completion passcode was verified, as an RFC 3339 timestamp in UTC: one of the three signals completion rests on. Null until you confirm completion.

dwell one of optional

How long the worker was at the location, as you sent it on completion: when it started, when it ended and the whole seconds between. null until you confirm completion.

DwellRecordor
telemetry_continuity one of optional

Whether the heartbeat stream held across the working window, as you sent it on completion: continuous or not, how many gaps, and the longest in seconds. null until you confirm completion.

TelemetryContinuityor
proof_of_service_id string · nullable optional

The id of the sealed proof for this assignment, starting with match_geo_aware_workforce_orchestrator_proof_, as GET /v1/allocation/assignments/{assignment_id}/proof returns it. null until the bundle is sealed.

exception_id string · nullable optional

The id of the exception raised on this assignment, starting with match_geo_aware_workforce_orchestrator_exception_, as listed at GET /v1/allocation/exceptions. null unless the signals disagreed and a person was asked.

cancelled_reason string · nullable optional

Why the assignment was cancelled: the reason sent with the cancellation, or worker_removed_from_cascade when ANCHOR or RAIL removed the worker. null unless status is cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationAssignmentsRaiseException({ assignmentId: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception', {
  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.MATCHApi(client)

result = api.allocation_assignments_raise_exception(assignment_id='match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAssignmentsRaiseException(assignment_id: 'match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception');
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.MatchApi;

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

var result = api.allocationAssignmentsRaiseException("match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception"))
    .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 MATCHApi(config);

var result = api.AllocationAssignmentsRaiseException(assignmentId: "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception");
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.MATCHAPI.AllocationAssignmentsRaiseException(ctx, "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/assignments/match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/raise_exception", 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": "match_geo_aware_workforce_orchestrator_assignment_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "assignment",
  "livemode": true,
  "mocked": true,
  "work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "accepted",
  "offer_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "geofence_entry": {
    "entered_at": "2026-09-01T09:00:00Z",
    "position": {
      "latitude": -90,
      "longitude": -180,
      "accuracy_metres": 0
    },
    "radius_metres": 0
  },
  "start_passcode_verified_at": "2026-09-01T09:00:00Z",
  "completion_passcode_verified_at": "2026-09-01T09:00:00Z",
  "dwell": {
    "started_at": "2026-09-01T09:00:00Z",
    "ended_at": "2026-09-01T09:00:00Z",
    "seconds": 0
  },
  "telemetry_continuity": {
    "continuous": true,
    "gap_count": 0,
    "longest_gap_seconds": 0
  },
  "proof_of_service_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "exception_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/work_orders/{work_order_id}/offer#

Offer a work order to the ranked pool

allocation.work_orders.offer

Send offers to the next round of the cascade. Every candidate considered is kept with the round they were in, so you can replay the ranking if it's ever challenged.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

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 work order in its new state.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationWorkOrdersOffer({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer', {
  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.MATCHApi(client)

result = api.allocation_work_orders_offer(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersOffer(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer');
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.MatchApi;

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

var result = api.allocationWorkOrdersOffer("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer"))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersOffer(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer");
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.MATCHAPI.AllocationWorkOrdersOffer(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/offer", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/work_orders/{work_order_id}/fill#

Fill a work order

allocation.work_orders.fill

Record that the headcount is met. A work order fills when the last assignment is made. Record it here; it is not inferred from a count.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

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 work order in its new state.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationWorkOrdersFill({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill', {
  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.MATCHApi(client)

result = api.allocation_work_orders_fill(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersFill(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill');
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.MatchApi;

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

var result = api.allocationWorkOrdersFill("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill"))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersFill(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill");
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.MATCHAPI.AllocationWorkOrdersFill(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/fill", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/allocation/work_orders/{work_order_id}/expire#

Expire a work order

allocation.work_orders.expire

Record that the time window passed with the headcount unmet. A supply gap is declared with the reason and sent to FLOW as sourcing demand rather than being dropped.

Path parameters

work_order_id string required

The order's identifier, from id on the order POST /v1/allocation/work_orders returned when you opened it, or on any order at GET /v1/allocation/work_orders. It starts with match_geo_aware_workforce_orchestrator_work_order_.

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 work order in its new state.

id string required

The order's identifier, returned in id by POST /v1/allocation/work_orders when you opened it. It starts with match_geo_aware_workforce_orchestrator_work_order_, never changes, and is the work_order_id every call on the order takes.

object always "work_order" required

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

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

status string required

An order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.

openofferedfilledcancelledunfilled
requester_id string required

The requester the work is for, as you sent it in requester_id at POST /v1/allocation/work_orders: their sub_ subject reference, the subject_ref ANCHOR verified at POST /v1/identity/verifications before the order opened.

service_category string required

The kind of service the work is, as you named it when you opened the order, such as field_maintenance. It names the service; occupation_code names who can do it.

occupation_code string required

The occupation the work needs, as a code from the occupation taxonomy, such as ng-7412. Any supply gap declared for the order carries it, so sourcing demand names the occupation.

headcount integer · minimum 1 required

The order stays open until this many acceptances are held.

headcount_filled integer · minimum 0 optional

How many acceptances the order holds so far, counted from 0. Read it against headcount to see how far the order is from filling.

urgency_class string required

How hard the cascade pushes, and how quickly an offer expires.

standardpriorityemergency
location GeoPoint required

A position reading. Accuracy is carried because a reading without it cannot be judged.

3 fields of GeoPoint
latitude number · minimum -90 · maximum 90 required

Latitude in decimal degrees, from -90 to 90. Negative values are south of the equator.

longitude number · minimum -180 · maximum 180 required

Longitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.

accuracy_metres integer · minimum 0 required

The radius the device reports as its confidence. A large value weakens every inference drawn from the point.

location_description string optional

The place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional; location is the position the ranking uses.

time_window TimeWindow required

When the work may be done. A window, never a single instant.

2 fields of TimeWindow
starts_at string · date-time required

When the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.

ends_at string · date-time required

When the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.

constraints array of Constraint optional

Hard constraints and soft preferences in one list, each naming which it is.

3 fields of Constraint
code string required

What the requirement is, as a code such as licence_electrical. A relaxation names the soft preference it dropped by this code, as its preference_code.

kind string required

hard for a requirement that is never relaxed and gates who can be ranked at all; soft for a preference the cascade may drop to widen the pool, with the drop recorded in the order's relaxations.

hardsoft
detail string optional

What the requirement means in words, such as Current electrical licence. Optional; the code is what a worker has to meet, and the detail is for people reading the order.

relaxations array of Relaxation optional

Every soft preference the cascade dropped, with the round it happened in.

4 fields of Relaxation
preference_code string required

The code of the soft preference that was dropped, as it appears in the order's constraints.

round integer · minimum 1 required

The cascade round the preference was dropped in, counted from 1. Match it to the same round in the ranking to see who the wider pool brought in.

relaxed_at string · date-time required

When the preference was dropped, as an RFC 3339 timestamp in UTC.

reason string required

Why the preference was dropped: pool_exhausted when the eligible pool ran out, expiry_approaching when the order's expires_at was near, headcount_unmet when acceptances fell short of headcount.

pool_exhaustedexpiry_approachingheadcount_unmet
escalation_bounds EscalationBounds optional

How far the search may widen. It's published on the order, so you can read the escalation policy.

3 fields of EscalationBounds
initial_radius_metres integer · minimum 0 required

The radius the first round searches within, in metres from location.

maximum_radius_metres integer · minimum 0 required

The furthest the radius may widen to, in metres from location. No round searches beyond it.

step_metres integer · minimum 1 required

How much the radius widens by on each escalation, in metres, at least 1. A round that widened is marked escalated in the ranking.

budget_ceiling Money optional
2 fields of Money
amount integer · int64 required

A whole number of the currency's minor unit, as defined by the ISO 4217 exponent. For NGN that is kobo, so 1234567 is twelve thousand three hundred and forty five naira and sixty seven kobo. A fractional value is refused with the code invalid_money_amount. Call GET /v1/currencies for the exponent of any currency. Never divide by a hundred by hand.

currency string required

ISO 4217 code.

expires_at string · date-time required

On expiry the order is declared unfilled with a reason, never left open.

cancelled_reason string · nullable optional

Why the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.

created_at string · date-time optional

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

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.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

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

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

const result = await api.allocationWorkOrdersExpire({ workOrderId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire', {
  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.MATCHApi(client)

result = api.allocation_work_orders_expire(work_order_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationWorkOrdersExpire(work_order_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire');
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.MatchApi;

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

var result = api.allocationWorkOrdersExpire("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire"))
    .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 MATCHApi(config);

var result = api.AllocationWorkOrdersExpire(workOrderId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire");
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.MATCHAPI.AllocationWorkOrdersExpire(ctx, "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/allocation/work_orders/match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/expire", 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": "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "work_order",
  "livemode": true,
  "mocked": true,
  "status": "open",
  "requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "service_category": "example",
  "occupation_code": "ng-7412",
  "headcount": 1,
  "urgency_class": "standard",
  "location": {
    "latitude": -90,
    "longitude": -180,
    "accuracy_metres": 0
  },
  "time_window": {
    "starts_at": "2026-09-01T09:00:00Z",
    "ends_at": "2026-09-01T09:00:00Z"
  },
  "expires_at": "2026-09-01T09:00:00Z",
  "headcount_filled": 0,
  "location_description": "example",
  "constraints": [
    {
      "code": "licence_electrical",
      "kind": "hard",
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "relaxations": [
    {
      "preference_code": "no_payee_destination",
      "round": 1,
      "relaxed_at": "2026-09-01T09:00:00Z",
      "reason": "pool_exhausted"
    }
  ],
  "escalation_bounds": {
    "initial_radius_metres": 0,
    "maximum_radius_metres": 0,
    "step_metres": 1
  },
  "budget_ceiling": {
    "amount": 1234567,
    "currency": "NGN"
  },
  "cancelled_reason": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/allocation/events#

List events

allocation.events.list

Read the append only record of everything MATCH is the authority for, oldest first. Every event here is one the webhook catalogue declares, so the code that handles a delivery handles a replay too.

Two ways to read it. Pass stream with after to replay one stream from the sequence you last handled. That is exact and needs no cursor: the sequence counts per organisation and per stream, so it is the only ordering you can rely on. Without stream, you get events across streams in the order they were recorded, paged with starting_after.

Query parameters

stream string optional

The id of the record whose history you want: a work order's from POST /v1/allocation/work_orders or an assignment's from POST /v1/allocation/assignments, with after beside it. Leave it out to read across every stream, paged with starting_after.

after integer optional

The sequence of the last event you handled on this stream, as sequence reads on each event; you get the events after it, and 0 reads the stream from its first event. Needs stream beside it, because a sequence counts within one stream.

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

Returns

A page of events.

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 StoredEvent required

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

13 fields of StoredEvent
id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Droomwork is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

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

stream string required

The id of the record this event is about, such as a work order's from POST /v1/allocation/work_orders (match_geo_aware_workforce_orchestrator_work_order_). sequence counts per organisation and per stream, so it means nothing without this beside it.

data object required

The record the event is about, in the shape its type names: a work order, an offer, an assignment, an exception, a proof of service, a supply gap or a cascade removal. The same body a webhook delivery carries.

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: stream (optional), after (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
const result = await api.allocationEventsList({ stream: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after: 0, limit: 25 });
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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.MATCHApi(client)

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
result = api.allocation_events_list(stream='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after=0, limit=25)
import os

import requests

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
$result = $api->allocationEventsList(stream: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after: 0, limit: 25);
<?php
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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.MatchApi;

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

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.allocationEventsList("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 0, 25, null);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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 MATCHApi(config);

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.AllocationEventsList(stream: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", after: 0, limit: 25);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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: stream (optional), after (optional), limit (optional), starting_after (optional)
result, _, err := client.MATCHAPI.AllocationEventsList(ctx).Stream("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").After(0).Limit(25).Execute()
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/events?stream=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&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": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "type": "run.payslip.calculated",
      "schema_version": 1,
      "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "sequence": 0,
      "occurred_at": "2026-09-01T09:00:00Z",
      "livemode": true,
      "mocked": true,
      "source": "anchor",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "event",
      "stream": "example",
      "data": {}
    }
  ],
  "has_more": true
}
GET/v1/allocation/events/{event_id}#

Retrieve an event

allocation.events.retrieve

Read one event. An identifier belonging to another organisation is not found rather than refused. A refusal would confirm it exists.

Path parameters

event_id string required

The event's identifier, from the id of an event you listed at GET /v1/allocation/events, received on a webhook, or read as settlement_event_id on a proof of service. It starts with evt_.

Returns

The event.

id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Droomwork is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

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

stream string required

The id of the record this event is about, such as a work order's from POST /v1/allocation/work_orders (match_geo_aware_workforce_orchestrator_work_order_). sequence counts per organisation and per stream, so it means nothing without this beside it.

data object required

The record the event is about, in the shape its type names: a work order, an offer, an assignment, an exception, a proof of service, a supply gap or a cascade removal. The same body a webhook delivery carries.

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 GET "https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationEventsRetrieve({ eventId: '{event_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D', {
  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.MATCHApi(client)

result = api.allocation_events_retrieve(event_id='{event_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationEventsRetrieve(event_id: '{event_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D');
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.MatchApi;

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

var result = api.allocationEventsRetrieve("{event_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D"))
    .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 MATCHApi(config);

var result = api.AllocationEventsRetrieve(eventId: "{event_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D");
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.MATCHAPI.AllocationEventsRetrieve(ctx, "{event_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/events/%7Bevent_id%7D", 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": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "type": "run.payslip.calculated",
  "schema_version": 1,
  "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "sequence": 0,
  "occurred_at": "2026-09-01T09:00:00Z",
  "livemode": true,
  "mocked": true,
  "source": "anchor",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "event",
  "stream": "example",
  "data": {}
}
GET/v1/allocation/audit_entries#

List audit entries

allocation.audit_entries.list

See who did what, newest first. You get one row per attempt, not per success. Refusals are the half to watch: repeated forbidden answers on one credential is what an attack looks like.

A read that succeeded is not recorded. A log holding every list call is mostly noise, and noise is what stops a log being read.

Query parameters

action string optional

Return only attempts at one method and route pattern, such as POST /v1/allocation/work_orders or POST /v1/allocation/assignments/{assignment_id}/completion, as action reads on each entry. Leave it out to get every action.

actor_id string optional

Return only one actor's attempts, by the id of the API key (key_, from GET /v1/api_keys), person (usr_) or staff account, as actor_id reads on each entry. Pair it with outcome=refused to watch one credential; leave it out for every actor.

resource string optional

Return only attempts on one kind of record, by the collection segment of the route as resource reads on each entry, such as work_orders, assignments or exceptions. Pair it with resource_id to narrow to one record; leave it out to get every kind.

resource_id string optional

Return only attempts on one record, by the id that was in the route, such as a work order's from POST /v1/allocation/work_orders (match_geo_aware_workforce_orchestrator_work_order_). Pair it with resource; leave it out for every record.

outcome string optional

Return only one outcome: succeeded (a 2xx answer), refused (a 4xx, the request was turned down) or failed (a 5xx, it went wrong on our side). Leave it out to get all three.

succeededrefusedfailed
recorded_after string optional

Return only entries recorded after this moment, as an RFC 3339 timestamp in UTC, matched against at on each entry; an entry at exactly this instant is left out. Leave it out to set no lower bound.

recorded_before string optional

Return only entries recorded before this moment, as an RFC 3339 timestamp in UTC, matched against at on each entry; an entry at exactly this instant is left out. Pair it with recorded_after to bound a window, or leave it out to set no upper bound.

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

Returns

A page of audit entries.

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 AuditEntry required

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

13 fields of AuditEntry
id string required

The entry's identifier. It starts with audit_entry_ and never changes; pass it to GET /v1/allocation/audit_entries/{audit_entry_id} to read this one entry, or as starting_after to page past it.

object always "audit_entry" required

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

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It describes the route and not the answer: a refusal and a replayed idempotent request are both attempts against a mocked route, and the log says so.

at string · date-time required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list is ordered by it, newest first, and recorded_after and recorded_before bound it.

request_id string required

The identifier of the call that made this attempt. It starts with req_ and is the same request_id an error response carries, so a refusal you were shown can be matched to its entry here.

actor_type string required

What kind of caller made the attempt: client for one of your API keys or OAuth clients, user for a signed-in person, staff for a Droomwork staff member on a support case, service for Droomwork acting for you.

actor_id string required

Who made the attempt, as the id of whatever actor_type names: your API key (key_, listed at GET /v1/api_keys) or the OAuth client_id, the person (usr_), the staff member, or the service acting for you. Filter the list by it with actor_id.

action string required

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record the attempt was aimed at, as the collection in the route, such as work_orders or assignments. null when the route names none.

resource_id string · nullable optional

The id of the record the route named, such as a work order's from POST /v1/allocation/work_orders (match_geo_aware_workforce_orchestrator_work_order_). null for an attempt on a collection, such as creating or listing.

outcome string required

How the attempt ended: succeeded for a 2xx answer, refused for a 4xx, failed for a 5xx on our side. Refusals are recorded too: filter GET /v1/allocation/audit_entries by actor_id and outcome=refused to watch one credential.

succeededrefusedfailed
status integer required

The HTTP status the caller was given, such as 201 or 403. outcome is read from it: 2xx is succeeded, 4xx refused, 5xx failed.

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: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
const result = await api.allocationAuditEntriesList({ action: 'POST /v1/allocation/work_orders', actorId: 'usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'work_orders', resourceId: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'succeeded', recordedAfter: '2026-01-01T00:00:00Z', recordedBefore: '2027-01-01T00:00:00Z', limit: 25 });
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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.MATCHApi(client)

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
result = api.allocation_audit_entries_list(action='POST /v1/allocation/work_orders', actor_id='usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource='work_orders', resource_id='match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome='succeeded', recorded_after='2026-01-01T00:00:00Z', recorded_before='2027-01-01T00:00:00Z', limit=25)
import os

import requests

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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\MATCHApi(new GuzzleHttp\Client(), $config);

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
$result = $api->allocationAuditEntriesList(action: 'POST /v1/allocation/work_orders', actor_id: 'usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'work_orders', resource_id: 'match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'succeeded', recorded_after: '2026-01-01T00:00:00Z', recorded_before: '2027-01-01T00:00:00Z', limit: 25);
<?php
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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.MatchApi;

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

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var result = api.allocationAuditEntriesList("POST /v1/allocation/work_orders", "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "work_orders", "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "succeeded", "2026-01-01T00:00:00Z", "2027-01-01T00:00:00Z", 25, null);
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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 MATCHApi(config);

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var result = api.AllocationAuditEntriesList(action: "POST /v1/allocation/work_orders", actorId: "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", resource: "work_orders", resourceId: "match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", outcome: "succeeded", recordedAfter: "2026-01-01T00:00:00Z", recordedBefore: "2027-01-01T00:00:00Z", limit: 25);
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
result, _, err := client.MATCHAPI.AllocationAuditEntriesList(ctx).Action("POST /v1/allocation/work_orders").ActorId("usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Resource("work_orders").ResourceId("match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Outcome("succeeded").RecordedAfter("2026-01-01T00:00:00Z").RecordedBefore("2027-01-01T00:00:00Z").Limit(25).Execute()
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/audit_entries?action=POST%20%2Fv1%2Fallocation%2Fwork_orders&actor_id=usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=work_orders&resource_id=match_geo_aware_workforce_orchestrator_work_order_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&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": "audit_entry_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "audit_entry",
      "livemode": true,
      "mocked": true,
      "at": "2026-09-01T09:00:00Z",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "actor_type": "example",
      "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "action": "example",
      "outcome": "succeeded",
      "status": 1,
      "resource": "example",
      "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
  ],
  "has_more": true
}
GET/v1/allocation/audit_entries/{audit_entry_id}#

Retrieve an audit entry

allocation.audit_entries.retrieve

Read one entry. An identifier belonging to another organisation is not found rather than refused. A refusal would confirm it exists.

Path parameters

audit_entry_id string required

The entry's identifier, from the id of an entry you listed at GET /v1/allocation/audit_entries. It starts with audit_entry_, and you never create one: an entry is recorded for every attempt to change something and every refusal.

Returns

The audit entry.

id string required

The entry's identifier. It starts with audit_entry_ and never changes; pass it to GET /v1/allocation/audit_entries/{audit_entry_id} to read this one entry, or as starting_after to page past it.

object always "audit_entry" required

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

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It describes the route and not the answer: a refusal and a replayed idempotent request are both attempts against a mocked route, and the log says so.

at string · date-time required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list is ordered by it, newest first, and recorded_after and recorded_before bound it.

request_id string required

The identifier of the call that made this attempt. It starts with req_ and is the same request_id an error response carries, so a refusal you were shown can be matched to its entry here.

actor_type string required

What kind of caller made the attempt: client for one of your API keys or OAuth clients, user for a signed-in person, staff for a Droomwork staff member on a support case, service for Droomwork acting for you.

actor_id string required

Who made the attempt, as the id of whatever actor_type names: your API key (key_, listed at GET /v1/api_keys) or the OAuth client_id, the person (usr_), the staff member, or the service acting for you. Filter the list by it with actor_id.

action string required

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record the attempt was aimed at, as the collection in the route, such as work_orders or assignments. null when the route names none.

resource_id string · nullable optional

The id of the record the route named, such as a work order's from POST /v1/allocation/work_orders (match_geo_aware_workforce_orchestrator_work_order_). null for an attempt on a collection, such as creating or listing.

outcome string required

How the attempt ended: succeeded for a 2xx answer, refused for a 4xx, failed for a 5xx on our side. Refusals are recorded too: filter GET /v1/allocation/audit_entries by actor_id and outcome=refused to watch one credential.

succeededrefusedfailed
status integer required

The HTTP status the caller was given, such as 201 or 403. outcome is read from it: 2xx is succeeded, 4xx refused, 5xx failed.

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 GET "https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, MATCHApi } from '@droomwork/sdk';

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

const result = await api.allocationAuditEntriesRetrieve({ auditEntryId: '{audit_entry_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D', {
  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.MATCHApi(client)

result = api.allocation_audit_entries_retrieve(audit_entry_id='{audit_entry_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D',
    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\MATCHApi(new GuzzleHttp\Client(), $config);

$result = $api->allocationAuditEntriesRetrieve(audit_entry_id: '{audit_entry_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D');
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.MatchApi;

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

var result = api.allocationAuditEntriesRetrieve("{audit_entry_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D"))
    .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 MATCHApi(config);

var result = api.AllocationAuditEntriesRetrieve(auditEntryId: "{audit_entry_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D");
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.MATCHAPI.AllocationAuditEntriesRetrieve(ctx, "{audit_entry_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/allocation/audit_entries/%7Baudit_entry_id%7D", 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": "audit_entry_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "audit_entry",
  "livemode": true,
  "mocked": true,
  "at": "2026-09-01T09:00:00Z",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "actor_type": "example",
  "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "action": "example",
  "outcome": "succeeded",
  "status": 1,
  "resource": "example",
  "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}