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 and not 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#Send what the worker's device recorded
allocation.heartbeats.create
This is how a worker's device reports in, built for a network that keeps dropping. A phone out of coverage hands over what it held when the link returns, and the receipt accounts for every reading one way or the other.
Send one batch for one worker, named by the sub_ reference you chose at POST /v1/identity/consent_tokens, with one entry per reading the device took. Back comes a receipt that accounts for every reading you sent, counted as accepted or named with its index.
Stamp recorded_at with when the device took the reading, not when you send the batch. Sending availability on a reading does not set it; the worker's own call at PUT /v1/allocation/availability/{worker_id} does.
The call answers 202 whether or not every reading was accepted, so read rejections and not only the status. The receipt hands the same worker_id back for GET /v1/allocation/supply_states/{worker_id}.
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 receipt for the batch, with every refused reading named by its index in what you sent.
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": "licence_electrical",
"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#See who can take work now
allocation.supply_states.list
Use this to see who on your workforce could take work right now, one record for each worker. Each record names the operational zone that worker resolved to, so a staffing view in your product can show where people are as well as how many.
You get one supply state for each worker, carrying their availability, their presence, which is what was last heard from them, and the time their last heartbeat arrived. A heartbeat is one reading taken at a moment their device recorded.
When you only want one worker, go straight to GET /v1/allocation/supply_states/{worker_id} with the worker_id from the record; it answers with the same object.
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
One supply state for each worker, enough to judge who to send a job to.
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": false,
"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}#Check one worker before you offer work
allocation.supply_states.retrieve
Use this when you have one worker in hand, from an offer or an assignment, and want their current supply state. It is what fills a single worker's detail view, where somebody reads one person's standing without pulling the whole workforce.
Put the worker's reference in the path and you get that one worker's supply state back, the same record the list carries for them, with nothing added and nothing left out.
The reference to pass is the sub_ subject reference you chose when you first recorded the worker with ANCHOR. MATCH issues no worker identifier of its own, so there is nothing to look up before you call this.
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 named worker's supply state, as it stands at the moment you asked.
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": false,
"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 the availability the worker chose
allocation.availability.update
This is the call behind the control a worker uses to say whether they can take work. One screen in your product can carry that control and show where the worker stands afterwards, because the answer is their whole supply state, not an acknowledgement.
You send the state they chose and name them as {worker_id} in the path. Back comes their supply state, and availability_set_at on it says when they set it.
{worker_id} is the sub_ subject reference you sent as subject_ref at POST /v1/identity/consent_tokens when the worker was first recorded.
Leave effective_at out and the change applies now. A future value schedules it instead, so read availability on the answer before you treat it as moved.
GET /v1/allocation/supply_states/{worker_id} answers with the same supply_state object, so one reader in your code handles both.
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 worker's supply state, carrying the availability they set and the time they set it.
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": false,
"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#See which positions were flagged, and why
allocation.integrity_flags.list
A flag is suspected position spoofing recorded against one worker, carrying each detection that fired and what it saw. Listing them lets your own product hold the review queue, so whoever weighs one has the whole flag in front of them.
Send nothing and every flag your organisation holds comes back, each one naming the worker it concerns, its status, the signals that fired with the time and detail of each, and its decision once one has been made.
A flag's worker_id is the sub_ subject reference ANCHOR holds for that person, and not an identifier of MATCH's own, so match a flag to somebody through what you stored when you recorded them.
Take a flag's id to POST /v1/allocation/integrity_flags/{integrity_flag_id}/adjudicate as integrity_flag_id when a person has decided it.
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 flags, each with the signals that fired and any decision made.
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": false,
"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#Decide whether the signals amount to spoofing
allocation.integrity_flags.adjudicate
Somebody has to decide whether what the detections saw amounts to spoofing, and this call records that decision. It is the only thing that moves a flag off open, so the judgement stays with a person in your organisation.
You name a flag you listed at GET /v1/allocation/integrity_flags and send the outcome you decided with the reason for it. What comes back is the whole flag, carrying a decision and a status no longer open.
Afterwards the flag carries its decision, and reads the same way at GET /v1/allocation/integrity_flags for anybody in your organisation who looks.
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 flag as it now stands, with its decision and the status it reached.
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": false,
"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#Every work order you have opened
allocation.work_orders.list
These are the work orders your organisation has opened, whatever state each one has reached. It is the read a dispatch screen in your own product is built on: what is still open, what was filled, and what lapsed with nobody found.
Send nothing and you get your work orders back, each one a single request for workers at one place inside one time window, carrying the state it has reached and the moment it lapses.
Read one in full at GET /v1/allocation/work_orders/{work_order_id}, passing a row's id in the path.
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
Your work orders, each with the state it has reached and when it lapses.
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": false,
"mocked": true,
"status": "open",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"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
Opening a work order is how a job you need done starts existing in MATCH. One order carries the demand whatever route it arrived by, and the identifier it answers with is what every later call takes.
Send who the work is for, the service, the occupation that can do it, how many people you need, where and when, how urgent it is and when the request lapses. You get the order back with an identifier beginning match_geo_aware_workforce_orchestrator_work_order_.
Have the requester's sub_ subject reference ready, the one ANCHOR verified at POST /v1/identity/verifications.
expires_at is not the end of time_window. It is when you stop wanting the order filled, and it can fall before the work is due to start.
The order opens in the open state, and POST /v1/allocation/work_orders/{work_order_id}/offer is what moves it next.
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 new order, open, with the identifier every later call on it takes.
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":"field_maintenance","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":100000000,"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":"field_maintenance","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":100000000,"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": "field_maintenance",
"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": 100000000,
"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": "field_maintenance", "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": 100000000, "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": "field_maintenance", "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": 100000000, "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":"field_maintenance","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":100000000,"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":"field_maintenance","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":100000000,"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": "field_maintenance",
"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": 100000000,
"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": "field_maintenance",
"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": 100000000,
"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": "field_maintenance",
"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": 100000000,
"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": false,
"mocked": true,
"status": "open",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"currency": "NGN"
},
"cancelled_reason": "example",
"created_at": "2026-09-01T09:00:00Z"
}
/v1/allocation/work_orders/{work_order_id}#Check where one order stands
allocation.work_orders.retrieve
This is one order as it stands, which is how a detail page in your own product shows a single piece of demand without keeping its own copy of the order.
Pass the order's identifier in the path and the whole order comes back: who it is for, the occupation, how many people it needs, the place and how precisely it was given, the time window, the expiry and the state it has reached.
status is what tells you which calls are still open to you. An order reading offered can be filled; one reading open can still be offered or declared unfilled.
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 order as it now stands, with the state it has reached and what it requires.
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": false,
"mocked": true,
"status": "open",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"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
Cancelling is how a job you no longer need stops being live demand. You pick the reason from a fixed set of four, so your own reporting can count why orders were cancelled instead of reading free text.
Send the reason you are cancelling as one of the four published codes. The call takes the order to cancelled, and the whole order comes back so your own screen can stop showing it as live demand.
Nothing else has to exist first: the identifier in the path is the order you opened at POST /v1/allocation/work_orders.
Once an order reads cancelled, offering, filling or expiring it is refused with conflict.
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 order after the cancellation, which is what your own records should now match.
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": false,
"mocked": true,
"status": "open",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"currency": "NGN"
},
"cancelled_reason": "example",
"created_at": "2026-09-01T09:00:00Z"
}
/v1/allocation/work_orders/{work_order_id}/ranking#Who was considered, and who was offered
allocation.rankings.retrieve
The ranking is the allocation record kept against one work order. A screen in your own product can show who was considered for a job and who was offered it, instead of only the assignment that came out of it.
You name the work order in the path and get its ranking back, one entry per pass of the cascade, each with the candidates on it and the radius it searched.
A candidate whose eligible is false carries no scores and no rank, so read eligible before you read a number and handle the nulls. The surge figures are placeholders in both realms, so don't act on the index.
Each offer at GET /v1/allocation/work_orders/{work_order_id}/offers carries the round it was made in, so you can line the offers up with the passes here. A candidate who was offered nothing carries non_offer_reason, which is what that worker is told.
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 work order's ranking, which is where you read who was considered and who was offered.
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#Follow the cascade for one order
allocation.offers.list
This is the record of who one work order has been put to, and in what order. It gives you the dispatcher's view of a job in flight: who was asked, who answered, and who is still holding it.
You get every offer made for the work order in the path, oldest first, each one naming its round, the worker it went to and whether that worker has answered yet.
Read the list whole and narrow it in your own code.
An unknown work order identifier is refused with not_found, so you get that refusal and not an empty list. Read one offer in full at GET /v1/allocation/offers/{offer_id}.
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
Every offer made for this work order, oldest first, with the round each belongs to
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": false,
"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}#Check where one offer stands
allocation.offers.retrieve
This answers what became of one offer: whether the worker still has it, and where it ended if it ended. It is the detail behind a single row of the cascade, and it is enough to build the worker's own view of the job they were asked to do.
You pass one offer's identifier and get that offer whole: the work order behind it, the worker it went to, its round, and where it stands. An offer is one job put to one worker in one round of the cascade.
Where closed_reason is present it carries the wording written for the worker. Show that wording instead of composing your own.
The cascade this offer sits in is at GET /v1/allocation/work_orders/{work_order_id}/offers.
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
One offer, the worker it went to, and where it now stands
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": false,
"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
This is where a worker says yes. It is the one call behind the accept button in their app, and it is the point at which a job stops being an ask and becomes work somebody is committed to.
Send the moment the worker accepted as accepted_at, and the position their device read if it had one. One call answers one offer, and the offer names the one worker it went to.
The offer in the path is the prerequisite; the cascade made it when the work order went out.
accepted_at is the worker's own moment, not the moment your call arrives, so send what their device recorded and not a reading from your own clock.
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 the acceptance created, at accepted, which is where every later step on this job starts.
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": false,
"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
This is where a worker says no, and says why. It is the other half of the accept button, and it is what separates a worker who turned the job down from one who never answered at all.
Send the reason the worker gave, and any words behind it as detail. You get the declined offer back, so your own record of the cascade and Droomwork's say the same thing.
The offer in the path is the prerequisite; the cascade made it when the work order went out.
reason is required and comes from a fixed set, so a worker who gave no explanation still needs a code chosen for them. Put what they actually said in detail, where a code alone would lose 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
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 same offer, now declined, with the round it belonged to
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": false,
"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#See how offers are spread
allocation.dispersion.retrieve
This read is about the spread of your offers and not about one piece of work. A fairness review in your own product can come from this one call, instead of counting offers work order by work order yourself.
Send nothing and one reading comes back: the minimum offers a worker is entitled to in the window, a row for each band of the population with its offer counts, and how many workers sit under the minimum.
mean_offers_per_worker is an average over a whole band, so a band can read above the minimum and still hold workers under it. workers_below_floor is the headcount to act on.
The report counts people and never names them. No worker identifier appears anywhere in it, so a short band tells you how many workers are under the minimum and not which ones.
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
One reading of offer dispersion, with the minimum in force and how many workers are under it.
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#See who is on which job
allocation.assignments.list
An assignment is one worker on one work order, and this is every one your organisation holds. From it a product can build the screen that answers who is out today, which order each of them is filling, and how far along each job has got.
Send nothing and the assignments come back, each carrying the worker_id of the person doing the work, the work_order_id it fills, and the status it has reached.
Read one in full at GET /v1/allocation/assignments/{assignment_id}, passing a row's id as assignment_id. A row's work_order_id is what you pass to GET /v1/allocation/work_orders/{work_order_id} to read the order behind it.
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, each naming the worker, the work order and the state reached.
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": false,
"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#Put work agreed elsewhere on the record
allocation.assignments.create
An assignment normally arrives with a filled work order, out of the cascade. Creating one directly covers the job you arranged outside that, over the phone or on site, so your product holds one shape of record for every job and one place to collect the evidence for it.
Send the work_order_id the job fills and the worker_id doing it. You get the assignment back in accepted, with an identifier beginning match_geo_aware_workforce_orchestrator_assignment_ that every later call on it takes.
Open the work order first at POST /v1/allocation/work_orders; its id is what you send here. The worker_id is the sub_ subject reference you chose for that person, never an offer or assignment identifier.
From here the assignment moves by its own calls, beginning with POST /v1/allocation/assignments/{assignment_id}/depart when the worker sets off.
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 you just created, in accepted, with the identifier every later call takes.
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": false,
"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}#Check where one job has got to
allocation.assignments.retrieve
This is one assignment in full, which is the record a question about a single job gets answered from. Your product can show whoever asked what state the job is in and which pieces of evidence have been written on it so far.
Send the assignment's identifier in the path and you get it back whole: the worker, the work order it fills, the state it has reached, and the arrival and completion evidence recorded against it.
Anything not yet recorded comes back null. Read those fields before you treat a job as further along than it is.
The sealed proof is a separate read at GET /v1/allocation/assignments/{assignment_id}/proof, and it answers conflict until the assignment is completed.
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
One assignment in full, with the evidence written on it so far.
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": false,
"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
Arrival is the moment a job stops being travel and becomes presence at the place the work is. Recording it gives you and the worker one agreed time for that, which is a different time from when the work itself began.
Send the geofence entry the worker's device read and the start passcode they were given on site. Both are required. You get the assignment back with its status now arrived.
The assignment has to exist already: its id comes from POST /v1/allocation/assignments or GET /v1/allocation/assignments.
The start passcode is write-only and is returned on no response, so keep your own copy of whatever you issued.
POST /v1/allocation/assignments/{assignment_id}/arrive reaches the same arrived state from en_route, with no signals on it.
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, which is the state a start is reached from.
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": false,
"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
Completion is where a job becomes something you can account for. It is the state the sealed proof of service is read from, so your product has one point at which work in progress becomes evidence you can hold.
Send the completion passcode, the dwell at the location and the telemetry continuity across the working window. All three are required. You get the assignment back with its status now completed. Nothing else has to exist first.
Compute dwell.seconds from the started_at and ended_at you send beside it; nothing recomputes it for you.
POST /v1/allocation/assignments/{assignment_id}/complete reaches the same completed state as a lifecycle step. Once the assignment is completed, read its proof at GET /v1/allocation/assignments/{assignment_id}/proof.
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, now completed, which is the point its proof becomes readable.
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": false,
"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#Call off a job you no longer need
allocation.assignments.cancel
Cancelling is how a job that is not going to happen leaves your open work. A screen built on status stops carrying it, so what is left on the list is work somebody still expects to be done.
Send the reason you are calling it off, and detail in your own words where the code alone does not say enough. You get the assignment back with its status now cancelled. Nothing else has to exist first.
reason is a fixed code and detail is yours to write. Keep worker_removed_from_cascade for where the worker lost standing in ANCHOR or RAIL; your own change of plan takes one of the other codes.
A cancelled assignment seals no proof, so GET /v1/allocation/assignments/{assignment_id}/proof answers conflict on it.
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 assignment, now cancelled, which is where a job that never ran ends.
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": false,
"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#Read the sealed record of finished work
allocation.proofs.retrieve
A finished assignment leaves one record behind, and this is how you read it. It gives each completed job a single object your own system can store, so the work and the order that asked for it stay tied together in your records.
You pass the assignment's id in the path and get back its proof of service, naming the assignment and the work_order_id of the order it filled.
The bundle is there once the assignment reaches completed. Ask for it while the assignment is still running and the call is refused with conflict.
No call in this API edits a proof or removes one. These two reads are the whole of what you can do with a sealed bundle, and completing the assignment is what brings one into being.
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 proof of service for one finished assignment, ready to store against your own record of the job.
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": false,
"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#Reconcile the work that was sealed
allocation.proofs.list
This is where your organisation's proofs of service are read together. One call covers many assignments, so a reconciliation does not have to walk your assignments one at a time asking each whether a proof exists.
You get back the sealed proofs your organisation holds, each one carrying the assignment_id of the job it belongs to, which is what ties a proof to the work your own system dispatched.
Do not narrow this list with the query. assignment_id and sealed_after are published on it and neither changes what comes back, so match on the proofs you read.
Read a proof against the assignment it belongs to at GET /v1/allocation/assignments/{assignment_id}/proof, passing the assignment_id you read off the row.
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
The sealed proofs your organisation holds, each one naming the assignment it belongs to.
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": false,
"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#Completions put to a person
allocation.exceptions.list
An exception is one assignment's completion put to a person to decide. Reading them gives your product an adjudication queue of its own, where somebody can see every completion that is outstanding and the argument on each one before they rule.
Send nothing and every exception your organisation holds comes back, each with the assignment_id it was raised against, its status, what each conflicting signal said, and the ruling where one has been made.
There is no single-exception read, so this list is where you open one. A row's id is what you send as exception_id to POST /v1/allocation/exceptions/{exception_id}/adjudicate.
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, each naming its assignment, the signals in conflict and any ruling made.
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": false,
"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#Decide whether the work happened
allocation.exceptions.adjudicate
This records a person's ruling on one exception, and it is the only call that writes one. Your own product can keep the whole review in one place, with the decision landing on the record the conflict is written on.
Send the outcome you decided and, in your own words, why. You get the exception back with the decision recorded on it and the status it now carries. The exception has to exist already: its id comes from GET /v1/allocation/exceptions.
accepted_signals is optional, and it is for the case where only some of the signals persuaded you; name them with the signal values from this exception's own conflicting_signals, which you read off the record.
An unknown exception_id is refused with not_found. Read the exception back at GET /v1/allocation/exceptions afterwards; there is no single-exception read.
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 exception with the decision now recorded on it and the state it has reached.
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": false,
"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#Why orders went unfilled
allocation.supply_gaps.list
A supply gap is one work order that went unfilled. Each one names the occupation asked for and the zone it fell in, so you can see which occupations and which zones your orders keep failing in, instead of reading them order by order.
You get every gap your organisation holds, each naming the work order it was declared for, the reason it went unfilled, how many offers had gone out by then, and when that was.
Take work_order_id to GET /v1/allocation/work_orders/{work_order_id} for the headcount, location, time window and constraints the order asked for.
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, each naming the order that went unfilled and why.
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 from other modules
allocation.readiness.retrieve
MATCH asks for facts that other modules establish, and this call is where it names them. So the order you build in is a decision you can take from the document, before a line of allocation code exists.
Send no parameters. Each row in rows names one fact MATCH asks for, the owner that establishes it when you run integrated, and the assurance it has to reach. always_enforced sits beside the rows and names the gates no attestation of yours lifts.
What comes back is MATCH's requirement, and not the progress of any one order. It takes no identifier and names no record of yours. One order's own state is on the order, at GET /v1/allocation/work_orders/{work_order_id}.
Nothing else waits on this call.
Returns
The facts MATCH asks for, each with its owning module and the assurance that fact needs.
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": "match",
"mode": "integrated",
"ready": true,
"rows": [
{
"fact": "anchored_subject",
"owner": "anchor",
"contract_row": "6.4.20",
"required": "attested",
"held": "attested",
"source": "integrated",
"satisfied": true,
"missing_because": null
},
{
"fact": "executed_engagement",
"owner": "rail",
"contract_row": "6.6.24",
"required": "attested",
"held": "attested",
"source": "integrated",
"satisfied": true,
"missing_because": null
},
{
"fact": "competence_assertion",
"owner": "proof",
"contract_row": "6.8.7",
"required": "attested",
"held": "attested",
"source": "integrated",
"satisfied": true,
"missing_because": null
}
],
"always_enforced": [
"consent_token",
"bank_account_validation",
"tax_identifier_validation",
"duplicate_detection"
],
"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#Say the work itself has begun
allocation.assignments.start
Being on site and working are two different things, and this is the step that says the work itself has begun. It leaves your product holding a start time separate from the arrival time, which is the pair a later question about the job is read against.
Send the assignment's identifier in the path and no request body. You get the assignment back with its status now in_progress, which is the state a completion is reached from.
Only an assignment in arrived can start. Either arrival call writes that state; one in any other state answers conflict.
POST /v1/allocation/assignments/{assignment_id}/complete is what moves it on from here.
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, now in_progress, with the work itself recorded as begun.
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": false,
"mocked": true,
"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"status": "in_progress",
"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#Mark the worker as on site
allocation.assignments.arrive
This is the lifecycle step for a worker who has reached the work location, and the one that publishes an event. A product watching events learns that somebody is on site without polling the assignment for it.
Send the assignment's identifier in the path and no request body. You get the assignment back with its status now arrived, and assignment.arrived published on the assignment's own event stream.
Only an assignment in en_route can arrive, which POST /v1/allocation/assignments/{assignment_id}/depart writes. One in any other state answers conflict.
Follow the event at GET /v1/allocation/events with stream set to the assignment's id. POST /v1/allocation/assignments/{assignment_id}/arrival reaches the same arrived state carrying the geofence entry and the start passcode.
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, now arrived, with assignment.arrived published on its own event stream.
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": false,
"mocked": true,
"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"status": "arrived",
"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 that the worker has set off
allocation.assignments.depart
This is the step that says a worker is on the way. Until it is recorded a job looks the same whether somebody has left for it or not, and en_route is the state your own screens can show travel from.
Send the assignment's identifier in the path and no request body. You get the assignment back with its status now en_route, the state it sits in while the worker travels to the work location.
Only an assignment in accepted can depart, which is the state every assignment is created in. One in any other state answers conflict.
Arrival is the next step along, at POST /v1/allocation/assignments/{assignment_id}/arrive.
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, now en_route, the state it sits in while the worker travels.
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": false,
"mocked": true,
"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"status": "en_route",
"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#Finish a job and seal its proof
allocation.assignments.complete
This takes a job out of your work in progress and puts a sealed proof of service behind it. From here a product can treat the assignment as finished work and read the bundle that stands for what happened on it.
Send the assignment's identifier in the path and no request body. You get the assignment back with its status now completed, and a proof of service is sealed for the job as assignment.completed is published.
Only an assignment in in_progress can complete, which POST /v1/allocation/assignments/{assignment_id}/start writes. One in any other state answers conflict.
Read the proof at GET /v1/allocation/assignments/{assignment_id}/proof, and find it among the rest at GET /v1/allocation/proofs.
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, now completed, with a sealed proof of service standing behind it.
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": false,
"mocked": true,
"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"status": "completed",
"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#Send a job in doubt to a person
allocation.assignments.raise_exception
Some jobs cannot be closed on the evidence collected against them, and this is the state for those. It keeps a job that is in doubt out of your finished work and in front of somebody who can decide it, instead of letting a weak record pass as a good one.
Send the assignment's identifier in the path and no request body. You get the assignment back with its status now exception, which is outside the states a proof of service is sealed from.
Only an assignment in in_progress or arrived can be sent this way. One in any other state answers conflict.
GET /v1/allocation/assignments/{assignment_id}/proof answers conflict while the assignment sits in exception, so nothing downstream reads a proof that was never sealed.
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, now in exception, outside the states a proof is sealed from.
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": false,
"mocked": true,
"work_order_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"worker_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"status": "exception",
"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#Take the order out to offer
allocation.work_orders.offer
This is the call that puts an order into offering, which is the state it has to reach before you can record it filled. Your own system decides when demand goes out, and the moment it went out is on the event stream afterwards.
Send no request body. The call takes the order from open to offered, and offer.made is appended to the order's own event stream.
The order has to be open, which is the state POST /v1/allocation/work_orders leaves it in.
Read the stream back at GET /v1/allocation/events with stream set to the order's id. An order that already reads offered is refused with conflict here, and from offered it can be filled or declared unfilled.
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 order in its new state, which is the state a fill is accepted from.
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": false,
"mocked": true,
"status": "offered",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"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
Filling is how you say the order has the people it needed and the hunt is over. An order reading filled is always a thing somebody recorded on purpose.
Send the order's id in the path and no request body. You get the whole order back, now reading filled, with the headcount and the window it was opened for unchanged.
The order has to be reading offered, which is where POST /v1/allocation/work_orders/{work_order_id}/offer left it.
Filling an order that is not reading offered is refused with conflict, and so is offering or expiring one that already reads filled.
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 order in its new state, which no further offer or expiry will move.
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": false,
"mocked": true,
"status": "filled",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"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
Expiring is how an order whose time ran out is declared instead of left hanging. An order sitting open forever reads the same as one still being worked, and this call is what separates the two for anybody looking at your product.
Send no request body. The call takes the order from open or offered to unfilled, and work_order.unfilled is appended to the order's own event stream.
The order has to be reading open or offered; nothing else has to exist first.
Expiring an order that already reads filled, cancelled or unfilled is refused with conflict. Nothing takes an order back to open, so demand you still have is a new order from POST /v1/allocation/work_orders.
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 order in its new state, which nothing takes back to open.
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": false,
"mocked": true,
"status": "unfilled",
"requester_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"service_category": "field_maintenance",
"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": "example",
"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": 100000000,
"currency": "NGN"
},
"cancelled_reason": "example",
"created_at": "2026-09-01T09:00:00Z"
}
/v1/allocation/events#Replay a work order's own history
allocation.events.list
MATCH records what happens on the work orders and assignments it holds, and this reads those events back. Every event type here is one the webhook catalogue declares, so the handler you wrote for a delivery can read a replay too.
Pass stream with the id of the work order or assignment whose history you want, and after the sequence you last handled; you get that record's later events in order. Leave stream out and events from every stream come back together, oldest first.
A sequence counts inside one stream, so a number kept from one record means nothing against another. Send after only with the stream you read it from, and page across streams with starting_after instead.
Read any one of them in full at GET /v1/allocation/events/{event_id}; each entry's id starts with evt_.
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 MATCH's events, oldest first, each naming the record it happened to.
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": false,
"mocked": true,
"source": "anchor",
"request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "event",
"stream": "example",
"data": {}
}
],
"has_more": true
}
/v1/allocation/events/{event_id}#Confirm what one event recorded
allocation.events.retrieve
An event is how MATCH records one thing that happened on a work order or an assignment, and this opens one of them on its own. Nothing rewrites an event once it is stored, so an identifier kept in your own log shows the same thing months from now, after the record it was about has moved on.
Send the event's identifier in the path; it starts with evt_. You get that one event, with stream naming the record it belongs to, sequence saying where in that record's history it sits, and data carrying the record as it stood.
To read what followed on that record, send its stream to GET /v1/allocation/events with this event's sequence as after. An identifier nothing holds answers not_found.
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 as it was stored, with the record it was about at that moment.
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": false,
"mocked": true,
"source": "anchor",
"request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "event",
"stream": "example",
"data": {}
}
/v1/allocation/audit_entries#Who dispatched the work, and what we refused
allocation.audit_entries.list
This names who opened a work order, who filled it, who cancelled an assignment, and who we turned down. One row per attempt, so who changed the work is answerable from our own record.
Filter by actor_id to follow one caller, by resource_id to follow one work order or assignment, by outcome for the refusals alone, or by a window of time. Entries come back newest first.
action has to match an entry's stored value exactly. Copy one off an entry you hold; an approximation answers with an empty page.
A read of yours that succeeded leaves no entry. A read that was refused does, and so does every attempt to change something, whether it went through or was turned down.
Read one in full at GET /v1/allocation/audit_entries/{audit_entry_id}.
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
One page of attempts, newest first, with who acted and how each ended.
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": false,
"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": "work_orders",
"resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
],
"has_more": true
}
/v1/allocation/audit_entries/{audit_entry_id}#Trace one attempt on the work
allocation.audit_entries.retrieve
One entry is one attempt on one record, and this opens it in full. The identifier never changes, so your own copy of an assignment can carry it and who cancelled that assignment stays answerable long afterwards.
Send the audit_entry_id in the path and one entry comes back: the moment, the caller behind it, what they attempted, the record it was aimed at, and the status they were answered with.
An attempt aimed at a collection, as opening a work order is, leaves resource_id null. Check it before you follow it.
The reason behind a refusal is not on an entry, and neither is anything the change did; read GET /v1/allocation/events for that.
Where resource_id names an assignment, open it at GET /v1/allocation/assignments/{assignment_id}. An identifier with no entry behind it answers not_found.
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
One attempt in full, naming who made it, what they aimed at, and how it ended.
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": false,
"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": "work_orders",
"resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}