Version 1.0.0
Droomwork MATCH GEO-AWARE WORKFORCE ORCHESTRATOR
Allocation of work to available people, and proof that the work happened.
https://sandbox.droomwork.ioDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…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?
/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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
worker_id
string
requiredThe 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
requiredOrdered by the client where possible. The server reconciles regardless.
7 fields of Heartbeat
recorded_at
string · date-time
requiredThe 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
optionalOmitted where the collection window was closed. Sending one anyway is refused rather than stored.
GeoPointorconnectivity
string
requiredThe 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.
offlinepoorfairgoodavailability
one of
optionalWhat 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.
AvailabilityStateorbattery_percent
integer · nullable · minimum 0 · maximum 100
optionalDevice condition. A dying battery explains a gap that would otherwise read as absence.
mock_location_detected
boolean · nullable
optionalOne integrity signal of four. On its own it suspends nobody.
device_integrity_attested
boolean · nullable
optionalWhether 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"
requiredAlways heartbeat_receipt. Tells you which kind of record you are looking at, so one handler can read any response.
worker_id
string
requiredThe 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
requiredHow 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
requiredHow 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
requiredTrue when readings arrived out of order or late and were reordered by their recorded time.
zone
one of
optionalThe zone the worker resolved to after reconciliation.
Zoneorpresence
string
optionalWhat 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.
reportingstaleghostvalidation
ValidationReport
requiredThe 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
requiredHow 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
requiredHow 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
requiredHow 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
optionalOne 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
requiredThe row in a file, or the line in an instruction set.
severity
string
requirederror means the entry failed and must be fixed. warning means it passed, but points at something you should confirm before you go ahead.
errorwarningcode
string
requiredWhy 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
requiredWhat was wrong with the entry, in plain words. Show it beside the row; it may change, so branch on code.
rejections
array of object
optionalOne entry per refused reading, indexed against the submitted batch.
3 fields
index
integer · minimum 1
requiredWhich reading was refused, as its position in the readings you sent. Counted from 1, so the first reading is 1.
reason
string
requiredWhy 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_thresholddetail
string
optionalA sentence saying what about this reading was refused, where the reason code alone would not tell you. Not always present.
Other responses
Errors it can return
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)
{
"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."
}
]
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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_assignmentpresence
string
optionalReturn 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.
reportingstaleghostzone
string
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of SupplyState
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
15 fields of SupplyState
object
always "supply_state"
requiredAlways supply_state. Tells you which kind of record you are looking at, so one handler can read any response.
worker_id
string
requiredThe 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
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredSet by the worker and never inferred. Neither movement nor silence changes it.
availableunavailableon_assignmentpresence
string
requiredWhat 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.
reportingstaleghostrankable
boolean
requiredFalse 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
optionalWhy 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_livenullposition
one of
optionalNull whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.
GeoPointorposition_withheld_because
string · nullable
optionalWhich 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_assignmentnullcollection_window
string
optionalWhether 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.
openclosedzone
one of
optionalThe operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.
Zoneorlast_heartbeat_at
string · date-time · nullable
optionalThe 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
optionalHow long without a heartbeat before an available worker becomes ghost supply.
availability_set_at
string · date-time · nullable
optionalWhen the worker last set their availability. Preserved across a ghost exclusion.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
next_cursor
string · nullable
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredThe 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"
requiredAlways supply_state. Tells you which kind of record you are looking at, so one handler can read any response.
worker_id
string
requiredThe 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
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredSet by the worker and never inferred. Neither movement nor silence changes it.
availableunavailableon_assignmentpresence
string
requiredWhat 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.
reportingstaleghostrankable
boolean
requiredFalse 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
optionalWhy 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_livenullposition
one of
optionalNull whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.
GeoPointorposition_withheld_because
string · nullable
optionalWhich 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_assignmentnullcollection_window
string
optionalWhether 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.
openclosedzone
one of
optionalThe operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.
Zoneorlast_heartbeat_at
string · date-time · nullable
optionalThe 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
optionalHow long without a heartbeat before an available worker becomes ghost supply.
availability_set_at
string · date-time · nullable
optionalWhen the worker last set their availability. Preserved across a ghost exclusion.
Other responses
Errors it can return
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)
{
"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"
}
/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
requiredThe 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
requiredSet by the worker and never inferred. Neither movement nor silence changes it.
availableunavailableon_assignmenteffective_at
string · date-time
optionalDefaults 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"
requiredAlways supply_state. Tells you which kind of record you are looking at, so one handler can read any response.
worker_id
string
requiredThe 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
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredSet by the worker and never inferred. Neither movement nor silence changes it.
availableunavailableon_assignmentpresence
string
requiredWhat 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.
reportingstaleghostrankable
boolean
requiredFalse 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
optionalWhy 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_livenullposition
one of
optionalNull whenever the collection window is closed. This is the readable form of the rule that location is held only during availability or assignment.
GeoPointorposition_withheld_because
string · nullable
optionalWhich 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_assignmentnullcollection_window
string
optionalWhether 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.
openclosedzone
one of
optionalThe operational zone the worker resolved to on their last accepted reading, with its id and name. null while they have not resolved to one.
Zoneorlast_heartbeat_at
string · date-time · nullable
optionalThe 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
optionalHow long without a heartbeat before an available worker becomes ghost supply.
availability_set_at
string · date-time · nullable
optionalWhen the worker last set their availability. Preserved across a ghost exclusion.
Other responses
Errors it can return
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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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.
openuphelddismissedworker_id
string
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of IntegrityFlag
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
10 fields of IntegrityFlag
id
string
requiredThe 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"
requiredAlways integrity_flag. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredopenuphelddismissedsignals
array of IntegritySignal
requiredEvery 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
requiredThe four independent detections. Agreement between them is what a flag rests on.
mock_locationdevice_integrityimplausible_movementnetwork_cross_checkfired_at
string · date-time
requiredWhen 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
requiredWhat the signal saw, in terms a person adjudicating can weigh.
suspends
boolean
requiredTrue only where more than one independent signal fired and a person upheld the flag. One signal on its own suspends nobody.
decision
one of
optionalThe 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.
Decisionorraised_at
string · date-time
optionalWhen the flag was raised, as an RFC 3339 timestamp in UTC. The signals it carries each have their own fired_at.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
next_cursor
string · nullable
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
outcome
string
requiredYour 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.
uphelddismissedreason
string
requiredWhy 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
optionalName 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
requiredThe 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"
requiredAlways integrity_flag. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredopenuphelddismissedsignals
array of IntegritySignal
requiredEvery 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
requiredThe four independent detections. Agreement between them is what a flag rests on.
mock_locationdevice_integrityimplausible_movementnetwork_cross_checkfired_at
string · date-time
requiredWhen 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
requiredWhat the signal saw, in terms a person adjudicating can weigh.
suspends
boolean
requiredTrue only where more than one independent signal fired and a person upheld the flag. One signal on its own suspends nobody.
decision
one of
optionalThe 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.
Decisionorraised_at
string · date-time
optionalWhen the flag was raised, as an RFC 3339 timestamp in UTC. The signals it carries each have their own fired_at.
Other responses
Errors it can return
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)
{
"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"
}
/v1/allocation/work_orders#List work orders
allocation.work_orders.list
Lists your work orders, newest first.
Query parameters
limit
integer
optionalHow 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
optionalThe 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
optionalReturn 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.
openofferedfilledcancelledunfilledoccupation_code
string
optionalReturn 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
optionalReturn 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
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of WorkOrder
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
21 fields of WorkOrder
id
string
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
next_cursor
string · nullable
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
requester_id
string
requiredWho 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
requiredThe 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
requiredThe 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
requiredHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe place in words a worker would recognise, such as Ikeja industrial estate, gate 3. Optional, and it never replaces location.
time_window
TimeWindow
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard 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
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredThe 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
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
reason
string
requiredWhy 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_cascadedetail
string
optionalMore 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
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
optionalOne 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"
requiredAlways ranking. Tells you which kind of record you are looking at, so one handler can read any response.
work_order_id
string
requiredThe 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
requiredThe version of the weights in force when this ranking ran.
rounds
array of RankingRound
requiredEvery 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
requiredWhich 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
requiredWhen 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
requiredWithin the bounds published on the work order.
escalated
boolean
optionalTrue where this round widened the radius over the previous one.
candidates
array of RankingCandidate
requiredEvery candidate considered, eligible or not. Stored in full.
8 fields of RankingCandidate
worker_id
string
requiredThe 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
requiredtrue 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
optionalEmpty for an eligible candidate.
components
one of
optionalThe five scores this candidate was ranked on: travel time, competence, reliability, fatigue and cost. Null for an ineligible candidate, who is never scored.
ScoreComponentsortotal_score
number · nullable
optionalThe 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
optionalThe candidate's position in this round, 1 being the top. Null for an ineligible candidate.
offered
boolean
optionaltrue 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
optionalWhy 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.
NonOfferReasonorsurge
object
optionalSurge 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
requiredWhere 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
requiredThe 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
Errors it can return
# 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)
{
"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
}
}
/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
requiredThe 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
optionalHow 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
optionalThe 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
optionalReturn 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.
madeaccepteddeclinedexpiredwithdrawnReturns
A page of offers.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Offer
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of Offer
id
string
requiredThe 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"
requiredAlways offer. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
requiredmadeaccepteddeclinedexpiredwithdrawnround
integer · minimum 1
requiredThe cascade round this offer belongs to.
rank
integer · minimum 1
optionalWhere the worker stood in the round's ranking when this offer was made, 1 being the top.
expires_at
string · date-time
requiredAn offer that is not answered by this time is treated as a decline.
closed_reason
one of
optionalSet where the offer ended without acceptance, including where the order filled elsewhere.
NonOfferReasonorassignment_id
string · nullable
optionalThe 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
optionalWhen the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
next_cursor
string · nullable
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways offer. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
requiredmadeaccepteddeclinedexpiredwithdrawnround
integer · minimum 1
requiredThe cascade round this offer belongs to.
rank
integer · minimum 1
optionalWhere the worker stood in the round's ranking when this offer was made, 1 being the top.
expires_at
string · date-time
requiredAn offer that is not answered by this time is treated as a decline.
closed_reason
one of
optionalSet where the offer ended without acceptance, including where the order filled elsewhere.
NonOfferReasonorassignment_id
string · nullable
optionalThe 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
optionalWhen the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.
Other responses
Errors it can return
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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
accepted_at
string · date-time
requiredWhen 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
optionalA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe 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
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
reason
string
requiredWhy 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_expireddetail
string
optionalOptional 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
requiredThe 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"
requiredAlways offer. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
requiredmadeaccepteddeclinedexpiredwithdrawnround
integer · minimum 1
requiredThe cascade round this offer belongs to.
rank
integer · minimum 1
optionalWhere the worker stood in the round's ranking when this offer was made, 1 being the top.
expires_at
string · date-time
requiredAn offer that is not answered by this time is treated as a decline.
closed_reason
one of
optionalSet where the offer ended without acceptance, including where the order filled elsewhere.
NonOfferReasonorassignment_id
string · nullable
optionalThe 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
optionalWhen the offer was made, as an RFC 3339 timestamp in UTC. Offers list in this order.
Other responses
Errors it can return
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)
{
"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"
}
/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
optionalThe 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_daysoccupation_code
string
optionalRestricts 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
optionalRestricts 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"
requiredAlways dispersion_report. Tells you which kind of record you are looking at, so one handler can read any response.
window
string
requiredThe 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_daysfloor_offers_per_window
integer · minimum 0
requiredThe defined minimum offer exposure an eligible worker is entitled to.
bands
array of DispersionBand
requiredOne 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
requiredWhich 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_taileligible_workers
integer · minimum 0
requiredHow 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
requiredHow many offers went to workers in this band during the window, whatever each offer's outcome.
mean_offers_per_worker
number
optionalThe 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
requiredEligible workers who did not reach the minimum exposure in this window.
workers_below_floor
integer · minimum 0
requiredHow 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
optionalWhen 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
Errors it can return
# 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)
{
"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"
}
/v1/allocation/assignments#List assignments
allocation.assignments.list
Lists your assignments, newest first.
Query parameters
limit
integer
optionalHow 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
optionalThe 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
optionalReturn 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_progresscompletedexceptioncancelledwork_order_id
string
optionalReturn 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
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Assignment
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
17 fields of Assignment
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
next_cursor
string · nullable
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body optional
work_order_id
string
optionalThe 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
optionalThe 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
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
geofence_entry
GeofenceEntry
requiredOne of the two signals arrival rests on.
3 fields of GeofenceEntry
entered_at
string · date-time
requiredWhen the worker's position first fell inside the geofence, as an RFC 3339 timestamp in UTC.
position
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
radius_metres
integer · minimum 0
optionalThe 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
requiredHeld by the requester and given to the worker on site. Never returned.
Returns
The assignment, now arrived.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
completion_passcode
string · write only
requiredNever returned on any response.
dwell
DwellRecord
requiredHow long the worker was at the location. One of the three signals completion rests on.
3 fields of DwellRecord
started_at
string · date-time
requiredWhen the worker's time at the location began, as an RFC 3339 timestamp in UTC.
ended_at
string · date-time
requiredWhen the worker's time at the location ended, as an RFC 3339 timestamp in UTC.
seconds
integer · minimum 0
requiredThe whole seconds between started_at and ended_at: the dwell duration completion is judged on. Never negative.
telemetry_continuity
TelemetryContinuity
requiredWhether 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
requiredtrue 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
requiredHow many times the heartbeat stream broke during the working window. 0 when it was continuous.
longest_gap_seconds
integer · minimum 0
requiredThe 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
optionalFree 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
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
reason
string
requiredWhy 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_cascadedetail
string
optionalMore about why, in your own words, for whoever reads the record later. Optional; reason is the fixed code.
Returns
The cancelled assignment.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways proof_of_service. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalThe 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
optionalThe 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
requiredNever fewer than two. One signal cannot establish that work occurred, so a bundle resting on one is not a bundle.
established_by
string
optionalHow 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_agreedadjudicationseal
Seal
requiredThe 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
requiredThe algorithm behind digest, such as sha256. Keep it with digest and key_reference: together they say exactly how this bundle was sealed.
digest
string
requiredThe 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
requiredWhen the bundle was sealed, as an RFC 3339 timestamp in UTC. Nothing added after this moment is inside the seal.
key_reference
string
optionalWhich signing key sealed it, so a seal stays verifiable across a key rotation.
settlement_event_id
string
optionalThe 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
Errors it can return
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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of ProofOfService
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
11 fields of ProofOfService
id
string
requiredThe 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"
requiredAlways proof_of_service. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalThe 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
optionalThe 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
requiredNever fewer than two. One signal cannot establish that work occurred, so a bundle resting on one is not a bundle.
established_by
string
optionalHow 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_agreedadjudicationseal
Seal
requiredThe 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
requiredThe algorithm behind digest, such as sha256. Keep it with digest and key_reference: together they say exactly how this bundle was sealed.
digest
string
requiredThe 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
requiredWhen the bundle was sealed, as an RFC 3339 timestamp in UTC. Nothing added after this moment is inside the seal.
key_reference
string
optionalWhich signing key sealed it, so a seal stays verifiable across a key rotation.
settlement_event_id
string
optionalThe 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
requiredtrue 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
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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.
openupheldrejectedassignment_id
string
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of AssignmentException
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
9 fields of AssignmentException
id
string
requiredThe 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"
requiredAlways assignment_exception. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredopenupheldrejectedconflicting_signals
array of object
requiredWhich signals disagreed, named rather than summarised as a failure.
2 fields
signal
string
requiredWhich 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_continuitydetail
string
requiredWhat 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
optionalThe 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.
Decisionorraised_at
string · date-time
optionalWhen 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
requiredtrue 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
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Body
outcome
string
requiredYour 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.
uphelddismissedreason
string
requiredWhy 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
optionalName 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
requiredThe 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"
requiredAlways assignment_exception. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredopenupheldrejectedconflicting_signals
array of object
requiredWhich signals disagreed, named rather than summarised as a failure.
2 fields
signal
string
requiredWhich 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_continuitydetail
string
requiredWhat 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
optionalThe 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.
Decisionorraised_at
string · date-time
optionalWhen 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
Errors it can return
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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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_declinedoccupation_code
string
optionalReturn 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
optionalReturn 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of SupplyGap
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
9 fields of SupplyGap
object
always "supply_gap"
requiredAlways supply_gap. Tells you which kind of record you are looking at, so one handler can read any response.
work_order_id
string
requiredThe 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
requiredWhy nobody took the work. Specific, because a gap without a reason cannot be acted on.
cascade_exhaustedexpiredno_eligible_supplyall_offers_declinedoccupation_code
string
requiredThe 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
optionalThe 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.
Zoneoreligible_considered
integer · minimum 0
optionalHow many workers the gates let through before the cascade ran out.
offers_made
integer · minimum 0
optionalHow 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
optionalWhether the sourcing demand event was accepted by FLOW.
declared_at
string · date-time
requiredWhen 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
requiredtrue 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
optionalWhere the next page starts, or null when this is the last page. Pass it back unchanged to continue.
Other responses
Errors it can return
# 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)
{
"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"
}
/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"
requiredAlways readiness_report. Tells you which kind of record you are looking at, so one handler can read any response.
module
string
requiredThe 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).
anchorproofrailflowmatchrunremitroutemode
string
requiredintegrated 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.
integratedstandaloneready
boolean
requiredtrue 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
requiredOne 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
requiredNamed for what it is, not for who supplies it.
owner
string
requiredThe sibling module that owns this fact when running integrated.
contract_row
string
optionalrequired
string
requiredHow 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.
attestedverifiedheld
one of
requiredAssuranceorsource
string · nullable
optionalintegratedstandalonenullsatisfied
boolean
requiredmissing_because
string · nullable
optionalnot_suppliedassurance_too_lownullalways_enforced
array of string
optionalGates 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
optionalNo ranking runs without one.
gates_applied
array of EligibilityGate
optionalThe gates the ranking applies, listed here so that nothing is checked at ranking time that was not reported first.
workers_without_executed_engagement
integer
optionalThey can hold availability and will never receive an offer.
workers_without_live_passport
integer
optionalHow 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
Errors it can return
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)
{
"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
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The assignment in its new state.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The assignment in its new state.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The assignment in its new state.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The assignment in its new state.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The assignment in its new state.
id
string
requiredThe 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"
requiredAlways assignment. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWork that has started resolves through completion or through exception. It is never cancelled, so the evidence of what happened survives.
accepteden_routearrivedin_progresscompletedexceptioncancelledgeofence_entry
one of
optionalThe 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.
GeofenceEntryorstart_passcode_verified_at
string · date-time · nullable
optionalWhen 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
optionalWhen 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
optionalHow 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.
DwellRecordortelemetry_continuity
one of
optionalWhether 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.
TelemetryContinuityorproof_of_service_id
string · nullable
optionalThe 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
optionalThe 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
optionalWhy 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The work order in its new state.
id
string
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The work order in its new state.
id
string
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
requiredThe 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
requiredA key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.
Returns
The work order in its new state.
id
string
requiredThe 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"
requiredAlways work_order. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredAn order is open until it is offered, filled, cancelled or declared unfilled. Nothing moves it back.
openofferedfilledcancelledunfilledrequester_id
string
requiredThe 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
requiredThe 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
requiredThe 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
requiredThe order stays open until this many acceptances are held.
headcount_filled
integer · minimum 0
optionalHow 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
requiredHow hard the cascade pushes, and how quickly an offer expires.
standardpriorityemergencylocation
GeoPoint
requiredA position reading. Accuracy is carried because a reading without it cannot be judged.
3 fields of GeoPoint
latitude
number · minimum -90 · maximum 90
requiredLatitude in decimal degrees, from -90 to 90. Negative values are south of the equator.
longitude
number · minimum -180 · maximum 180
requiredLongitude in decimal degrees, from -180 to 180. Negative values are west of the Greenwich meridian.
accuracy_metres
integer · minimum 0
requiredThe radius the device reports as its confidence. A large value weakens every inference drawn from the point.
location_description
string
optionalThe 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
requiredWhen the work may be done. A window, never a single instant.
2 fields of TimeWindow
starts_at
string · date-time
requiredWhen the window opens: the earliest the work may start, as an RFC 3339 timestamp in UTC.
ends_at
string · date-time
requiredWhen the window closes: the latest the work may finish, as an RFC 3339 timestamp in UTC.
constraints
array of Constraint
optionalHard constraints and soft preferences in one list, each naming which it is.
3 fields of Constraint
code
string
requiredWhat 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
requiredhard 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.
hardsoftdetail
string
optionalWhat 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
optionalEvery soft preference the cascade dropped, with the round it happened in.
4 fields of Relaxation
preference_code
string
requiredThe code of the soft preference that was dropped, as it appears in the order's constraints.
round
integer · minimum 1
requiredThe 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
requiredWhen the preference was dropped, as an RFC 3339 timestamp in UTC.
reason
string
requiredWhy 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_unmetescalation_bounds
EscalationBounds
optionalHow 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
requiredThe radius the first round searches within, in metres from location.
maximum_radius_metres
integer · minimum 0
requiredThe furthest the radius may widen to, in metres from location. No round searches beyond it.
step_metres
integer · minimum 1
requiredHow 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
optional2 fields of Money
amount
integer · int64
requiredA 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
requiredISO 4217 code.
expires_at
string · date-time
requiredOn expiry the order is declared unfilled with a reason, never left open.
cancelled_reason
string · nullable
optionalWhy the order was cancelled, as stated when it was cancelled. null while the order has not been cancelled.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/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)
{
"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"
}
/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
optionalThe 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
optionalThe 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
optionalHow 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
optionalThe 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of StoredEvent
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of StoredEvent
id
string
requiredThe 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
requiredWhat 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
requiredThe 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
requiredThe 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
requiredPer 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
requiredWhen the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.
request_id
string
optionalThe 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
requiredWhich realm the event happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhich 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.
anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligenceobject
always "event"
requiredAlways event. Tells you which kind of record you are looking at, so one handler can read any response.
stream
string
requiredThe 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
requiredThe 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
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# query parameters: 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)
{
"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
}
/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
requiredThe 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
requiredThe 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
requiredWhat 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
requiredThe 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
requiredThe 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
requiredPer 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
requiredWhen the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.
request_id
string
optionalThe 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
requiredWhich realm the event happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhich 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.
anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligenceobject
always "event"
requiredAlways event. Tells you which kind of record you are looking at, so one handler can read any response.
stream
string
requiredThe 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
requiredThe 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
Errors it can return
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)
{
"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": {}
}
/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
optionalReturn 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
optionalReturn 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
optionalReturn 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
optionalReturn 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
optionalReturn 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.
succeededrefusedfailedrecorded_after
string
optionalReturn 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
optionalReturn 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
optionalHow 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
optionalThe 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of AuditEntry
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of AuditEntry
id
string
requiredThe 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"
requiredAlways audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm the action happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhen 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
requiredThe 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
requiredWhat 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
requiredWho 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
requiredWhat was attempted, as the method and the route pattern.
resource
string · nullable
optionalThe 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
optionalThe 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
requiredHow 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.
succeededrefusedfailedstatus
integer
requiredThe 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
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# query parameters: 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)
{
"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
}
/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
requiredThe 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
requiredThe 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"
requiredAlways audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm the action happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhen 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
requiredThe 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
requiredWhat 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
requiredWho 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
requiredWhat was attempted, as the method and the route pattern.
resource
string · nullable
optionalThe 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
optionalThe 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
requiredHow 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.
succeededrefusedfailedstatus
integer
requiredThe 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
Errors it can return
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)
{
"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"
}