Version 1.0.0
Droomwork FLOW ENTERPRISE
Sourcing and screened leads across Nigerian hiring channels.
https://sandbox.droomwork.ioDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…FLOW gets one vacancy in front of candidates: send us a requisition, we broadcast it to every channel you've configured, and you get back leads who have already answered your knockout questions.
What you should know before you start
This is not a job board. There is no destination candidates visit. Your requisition goes out into other people's channels, and the Droomwork talent pool is one of those channels, not a special case.
Nothing broadcasts on a guess. Where a field was inferred and not stated, it comes back for you to confirm. Compensation, headcount, location and start date never go out on inference alone, and publishing is refused while any of the four is unconfirmed.
Channel copy cannot invent a fact. Pay figures, employer legal names, statutory claims and requirements in your channel copy are compared against the requisition, exactly. A mismatch fails the render and is logged. Nothing reaches a channel saying something the requisition did not.
Screening is a conversation. The outcome is a rule. Candidates answer your knockout questions conversationally, but pass and fail come from versioned deterministic rules. The screening record shows what was extracted from each answer and what the rules decided as two separate things, so you can see which one you disagree with.
A spend ceiling blocks before anything is computed. When a broadcast would exceed a ceiling, it is refused, and no charge is calculated or debited.
A requisition is versioned, and every lead remembers which version made it. An edit after first broadcast creates a new version. A lead stays bound to the version that produced it, so you can reproduce a shortlist.
Getting started
Create a requisition. Confirm anything that was inferred. Publish it. Read the leads.
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/sourcing/requisitions#Every role you are hiring for
sourcing.requisitions.list
This is every requisition your organisation holds, whatever state each one has reached. A hiring board in your product is drawn from this single read, so a screen showing what is open, what is waiting on confirmation and what has closed keeps no record of its own.
Send no request body and the requisitions come back, each one whole: the role, the headcount, where it stands and which version it is on.
Read one in full at GET /v1/sourcing/requisitions/{requisition_id}, passing a row's id as requisition_id.
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
optionalOnly requisitions in one state: draft, pending_confirmation (an inferred field awaits confirmation), under_review (a contested case, with a reviewer), quarantined, published or closed. Leave it out to get every state.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedReturns
Your requisitions, each one whole, enough to draw a hiring board from.
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 Requisition
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
16 fields of Requisition
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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.
Other responses
Errors it can return
# query parameters: limit (optional), starting_after (optional), status (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(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.sourcingRequisitionsList({ limit: 25, status: 'pending_confirmation' });// query parameters: limit (optional), starting_after (optional), status (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation', {
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.FLOWApi(client)
# query parameters: limit (optional), starting_after (optional), status (optional)
result = api.sourcing_requisitions_list(limit=25, status='pending_confirmation')import os
import requests
# query parameters: limit (optional), starting_after (optional), status (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation',
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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional), starting_after (optional), status (optional)
$result = $api->sourcingRequisitionsList(limit: 25, status: 'pending_confirmation');<?php
// query parameters: limit (optional), starting_after (optional), status (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation');
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.FlowApi;
import com.droomwork.sdk.model.*;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.sourcingRequisitionsList(25, null, FlowRequisitionStatus.fromValue("pending_confirmation"));// query parameters: limit (optional), starting_after (optional), status (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation"))
.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 FLOWApi(config);
// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.SourcingRequisitionsList(limit: 25, status: FlowRequisitionStatus.PendingConfirmation);// 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/sourcing/requisitions?limit=25&status=pending_confirmation");
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.FLOWAPI.SourcingRequisitionsList(ctx).Limit(25).Status(droomwork.FlowRequisitionStatus("pending_confirmation")).Execute()// query parameters: limit (optional), starting_after (optional), status (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/requisitions?limit=25&status=pending_confirmation", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "pending_confirmation",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
],
"has_more": true
}
/v1/sourcing/requisitions#Open a role you want to fill
sourcing.requisitions.create
This opens a role you are hiring for. Everything that follows for that hire hangs off the identifier it hands back: the adverts, the broadcasts, the screening questions and every lead. So your own system keeps one string per vacancy and can ask us about anything that happened to it.
A job title and a headcount are enough to open one. You get the whole requisition back, with an id starting flow_enterprise_requisition_ to keep.
Nothing has to exist first. One requisition is one role, so four hires of the same job are one call with headcount set to four and not four calls.
Carry the id forward as requisition_id, to POST /v1/sourcing/requisitions/{requisition_id}/confirm_inferences and then POST /v1/sourcing/requisitions/{requisition_id}/publish.
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
job_title
string
requiredThe title of the role. Any title people use locally works: it is mapped to an occupation code, and the code comes back for you to confirm if you didn't send one.
occupation_code
string
optionalOmit and it is inferred from the title, then returned for confirmation.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. One of the four fields that never broadcast on inference alone.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date
optionalThe date the role starts, as YYYY-MM-DD. If it has to be inferred it comes back in inferred_fields, and it can't broadcast until you confirm it.
knockout_criteria
array of KnockoutRuleInput
optionalThe questions every candidate must answer and the rule that decides pass or fail for each. Asked conversationally; judged by the rule exactly as you wrote it.
2 fields of KnockoutRuleInput
question
string
requiredWhat the candidate is asked. It may be phrased differently in the conversation.
rule
string
requiredThe deterministic test applied to the extracted answer.
description
string
optionalThe role in your own words: duties, requirements, anything a candidate should know. Channel copy is drawn from it, and no copy may claim what the requisition doesn't.
Returns
The new requisition, carrying the identifier every later call about this role needs.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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/sourcing/requisitions" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"job_title":"example","headcount":1,"occupation_code":"ng-7412","employment_type":"permanent","compensation":{"amount":{"amount":7500000,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"Lagos","geofence_radius_metres":1},"starts_on":"2026-09-01","knockout_criteria":[{"question":"example","rule":"years_experience >= 3"}],"description":"example"}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingRequisitionsCreate({
idempotencyKey: crypto.randomUUID(),
flowRequisitionCreateRequest: {"jobTitle":"example","headcount":1,"occupationCode":"ng-7412","employmentType":"permanent","compensation":{"amount":{"amount":7500000,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"Lagos","geofenceRadiusMetres":1},"startsOn":"2026-09-01","knockoutCriteria":[{"question":"example","rule":"years_experience >= 3"}],"description":"example"},
});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"knockout_criteria": [
{
"question": "example",
"rule": "years_experience >= 3"
}
],
"description": "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.FLOWApi(client)
result = api.sourcing_requisitions_create(body={"job_title": "example", "headcount": 1, "occupation_code": "ng-7412", "employment_type": "permanent", "compensation": {"amount": {"amount": 7500000, "currency": "NGN"}, "period": "monthly", "negotiable": True}, "location": {"description": "example", "state": "Lagos", "geofence_radius_metres": 1}, "starts_on": "2026-09-01", "knockout_criteria": [{"question": "example", "rule": "years_experience >= 3"}], "description": "example"})import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"job_title": "example", "headcount": 1, "occupation_code": "ng-7412", "employment_type": "permanent", "compensation": {"amount": {"amount": 7500000, "currency": "NGN"}, "period": "monthly", "negotiable": True}, "location": {"description": "example", "state": "Lagos", "geofence_radius_metres": 1}, "starts_on": "2026-09-01", "knockout_criteria": [{"question": "example", "rule": "years_experience >= 3"}], "description": "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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingRequisitionsCreate($idempotencyKey, json_decode('{"job_title":"example","headcount":1,"occupation_code":"ng-7412","employment_type":"permanent","compensation":{"amount":{"amount":7500000,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"Lagos","geofence_radius_metres":1},"starts_on":"2026-09-01","knockout_criteria":[{"question":"example","rule":"years_experience >= 3"}],"description":"example"}', true));<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions');
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 => '{"job_title":"example","headcount":1,"occupation_code":"ng-7412","employment_type":"permanent","compensation":{"amount":{"amount":7500000,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"Lagos","geofence_radius_metres":1},"starts_on":"2026-09-01","knockout_criteria":[{"question":"example","rule":"years_experience >= 3"}],"description":"example"}',
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingRequisitionsCreate(idempotencyKey, body);var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions"))
.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("""
{
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"knockout_criteria": [
{
"question": "example",
"rule": "years_experience >= 3"
}
],
"description": "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 FLOWApi(config);
var result = api.SourcingRequisitionsCreate(idempotencyKey, body);using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"knockout_criteria": [
{
"question": "example",
"rule": "years_experience >= 3"
}
],
"description": "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.FLOWAPI.SourcingRequisitionsCreate(ctx).IdempotencyKey(key).FlowRequisitionCreateRequest(body).Execute()body := strings.NewReader(`{
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"knockout_criteria": [
{
"question": "example",
"rule": "years_experience >= 3"
}
],
"description": "example"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "pending_confirmation",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/requisitions/{requisition_id}#Check one role as it stands
sourcing.requisitions.retrieve
This answers with one role in full as Droomwork holds it: what it pays, where the work is, and what stage it has reached. So a page in your product that opens a single vacancy needs this call and nothing else, and code about to act on a role can check where it stands first.
Send the requisition's identifier in the path. The whole record comes back, the same object a page of the list is made of.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
Query parameters
version
integer
optionalWhich version of the requisition to read, from 1 upward. Leave it out for the current one; pass a lead's requisition_version (from GET /v1/sourcing/leads/{lead_id}) or a broadcast's to read the version that produced it.
Returns
The role as we hold it, in full, with the stage it has reached.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
# query parameters: version (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=1" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: version (optional)
const result = await api.sourcingRequisitionsRetrieve({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', version: 1 });// query parameters: version (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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.FLOWApi(client)
# query parameters: version (optional)
result = api.sourcing_requisitions_retrieve(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', version=1)import os
import requests
# query parameters: version (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: version (optional)
$result = $api->sourcingRequisitionsRetrieve(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', version: 1);<?php
// query parameters: version (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: version (optional)
var result = api.sourcingRequisitionsRetrieve("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 1);// query parameters: version (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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 FLOWApi(config);
// query parameters: version (optional)
var result = api.SourcingRequisitionsRetrieve(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", version: 1);// query parameters: version (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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: version (optional)
result, _, err := client.FLOWAPI.SourcingRequisitionsRetrieve(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Version(1).Execute()// query parameters: version (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z?version=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)
{
"id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "pending_confirmation",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/requisitions/{requisition_id}/confirm_inferences#Confirm the fields that were inferred
sourcing.requisitions.confirm_inferences
This is the sign-off on everything FLOW worked out for itself on a role, and it is the step that turns a new requisition into one you can publish. So your product can put one approval screen in front of a hiring manager and take every answer they give in a single request.
Send the requisition's id in the path and your confirmations in the body. The requisition comes back in draft, no longer pending_confirmation.
The role has to be one still waiting on this, from POST /v1/sourcing/requisitions. Answer every outstanding field in this one call: confirmations takes as many entries as you need, and a second call on the same role answers conflict.
Then publish at POST /v1/sourcing/requisitions/{requisition_id}/publish.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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
confirmations
array of object
requiredOne entry for each field you are confirming: the field's name, whether you accept the inferred value, and the corrected value when you don't. At least one entry.
3 fields
field
string
requiredThe name of the inferred field you are answering for, exactly as it appears in the requisition's inferred_fields.
accepted
boolean
requiredtrue to accept the inferred value as it stands, false to reject it. When you reject, send the right value in corrected_value.
corrected_value
string
optionalSupply this when the inference was wrong.
Returns
The requisition as it now stands, ready for you to publish.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"confirmations":[{"field":"gross.amount","accepted":true,"corrected_value":"example"}]}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingRequisitionsConfirmInferences({
requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
idempotencyKey: crypto.randomUUID(),
flowConfirmInferencesRequest: {"confirmations":[{"field":"gross.amount","accepted":true,"correctedValue":"example"}]},
});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"confirmations": [
{
"field": "gross.amount",
"accepted": true,
"corrected_value": "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.FLOWApi(client)
result = api.sourcing_requisitions_confirm_inferences(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"confirmations": [{"field": "gross.amount", "accepted": True, "corrected_value": "example"}]})import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"confirmations": [{"field": "gross.amount", "accepted": True, "corrected_value": "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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingRequisitionsConfirmInferences($idempotencyKey, json_decode('{"confirmations":[{"field":"gross.amount","accepted":true,"corrected_value":"example"}]}', true));<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences');
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 => '{"confirmations":[{"field":"gross.amount","accepted":true,"corrected_value":"example"}]}',
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingRequisitionsConfirmInferences("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences"))
.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("""
{
"confirmations": [
{
"field": "gross.amount",
"accepted": true,
"corrected_value": "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 FLOWApi(config);
var result = api.SourcingRequisitionsConfirmInferences(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"confirmations": [
{
"field": "gross.amount",
"accepted": true,
"corrected_value": "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.FLOWAPI.SourcingRequisitionsConfirmInferences(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).FlowConfirmInferencesRequest(body).Execute()body := strings.NewReader(`{
"confirmations": [
{
"field": "gross.amount",
"accepted": true,
"corrected_value": "example"
}
]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/confirm_inferences", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "draft",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/requisitions/{requisition_id}/publish#Release a role for broadcast
sourcing.requisitions.publish
This is the release step for a role: afterwards it is one you are willing to show candidates. So your product can keep the writing and the approving to itself, and the release itself becomes one event your other systems can act on.
Send the requisition's id in the path and no request body. The requisition comes back in published, and a requisition.published event goes to your webhook endpoints.
A role has to have been through POST /v1/sourcing/requisitions/{requisition_id}/confirm_inferences first. One still waiting on that, or one being held, answers conflict.
Broadcasting is its own call: POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, where you pick the channels. The leads that come of it are at GET /v1/sourcing/leads.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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 requisition, now one you can hand to your channels.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingRequisitionsPublish({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish', {
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.FLOWApi(client)
result = api.sourcing_requisitions_publish(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingRequisitionsPublish(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingRequisitionsPublish("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish"))
.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 FLOWApi(config);
var result = api.SourcingRequisitionsPublish(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish");
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.FLOWAPI.SourcingRequisitionsPublish(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/publish", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "published",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/requisitions/{requisition_id}/close#Record that the hiring is over
sourcing.requisitions.close
Closing is how you record that a role is no longer being hired for. What the requisition produced stays where it is, so a role you filled in March still reads back as the role it was.
Send the requisition's id in the path and no request body. You get the requisition back in closed, and no call takes it out of that state.
The requisition has to be published. One still in draft, or waiting on its inferred fields being confirmed, answers conflict.
Nothing is removed by closing. The broadcasts and the capture links it made read back at their own addresses afterwards.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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 requisition, with every lead and advert it produced still readable.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingRequisitionsClose({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close', {
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.FLOWApi(client)
result = api.sourcing_requisitions_close(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingRequisitionsClose(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingRequisitionsClose("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close"))
.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 FLOWApi(config);
var result = api.SourcingRequisitionsClose(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close");
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.FLOWAPI.SourcingRequisitionsClose(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/close", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "closed",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/imports#Upload requisitions in bulk
sourcing.imports.create
This takes a file of roles instead of one request for each, for a recruiter who already keeps their vacancies in a spreadsheet. One upload opens every requisition the file holds and names the rows it could not read.
Send the file as the file part of a multipart/form-data body. You get the upload back with its own id, its validation_report, and requisition_ids for every requisition the file opened.
Nothing has to exist first.
An upload's id and a requisition's id both start flow_enterprise_requisition_, so the value on its own will not say which of the two you are holding.
Read the upload again at GET /v1/sourcing/imports/{import_id}, where its report outlives this response.
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 new upload, with the report the corrections come from.
id
string
requiredThe upload's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/imports and pass it as import_id to read the upload back; the requisitions it created are in requisition_ids.
object
always "requisition_import"
requiredAlways requisition_import. 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
requiredWhere the upload is: validating while its rows are checked, pending_acceptance once the report is ready for you, then accepted or rejected as the outcome. Read validation_report for every row that failed and why.
validatingpending_acceptanceacceptedrejectedrequisition_ids
array of string
optionalThe identifier of every requisition the upload created. Each behaves like one you created on its own, and reads at GET /v1/sourcing/requisitions/{requisition_id}.
validation_report
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.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/imports" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingImportsCreate({});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/imports', {
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.FLOWApi(client)
result = api.sourcing_imports_create()import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/imports',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingImportsCreate();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/imports');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingImportsCreate();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/imports"))
.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 FLOWApi(config);
var result = api.SourcingImportsCreate();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/imports");
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.FLOWAPI.SourcingImportsCreate(ctx).Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/imports", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition_import",
"livemode": false,
"mocked": true,
"status": "validating",
"validation_report": {
"entry_count": 0,
"error_count": 0,
"warning_count": 0,
"entries": [
{
"index": 1,
"severity": "error",
"code": "invalid_money_amount",
"detail": "The payee has no verified destination, so this line cannot be paid."
}
]
},
"requisition_ids": [
"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
]
}
/v1/sourcing/imports/{import_id}#Read back a file you uploaded
sourcing.imports.retrieve
A bulk upload is one file of requisitions together with the check that was run over it. Reading it back is how your product tells somebody which rows went through and which did not, long after the upload itself.
Send the upload's id in the path. You get the upload with its validation_report and requisition_ids, one identifier for every requisition the file created.
An upload's id and a requisition's id both start flow_enterprise_requisition_, so the value on its own will not tell you which of the two you are holding.
Path parameters
import_id
string
requiredThe upload's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/imports when you uploaded the file.
Returns
The same upload, with its report, as it stands now.
id
string
requiredThe upload's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/imports and pass it as import_id to read the upload back; the requisitions it created are in requisition_ids.
object
always "requisition_import"
requiredAlways requisition_import. 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
requiredWhere the upload is: validating while its rows are checked, pending_acceptance once the report is ready for you, then accepted or rejected as the outcome. Read validation_report for every row that failed and why.
validatingpending_acceptanceacceptedrejectedrequisition_ids
array of string
optionalThe identifier of every requisition the upload created. Each behaves like one you created on its own, and reads at GET /v1/sourcing/requisitions/{requisition_id}.
validation_report
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.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingImportsRetrieve({ importId: '{import_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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.FLOWApi(client)
result = api.sourcing_imports_retrieve(import_id='{import_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingImportsRetrieve(import_id: '{import_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingImportsRetrieve("{import_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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 FLOWApi(config);
var result = api.SourcingImportsRetrieve(importId: "{import_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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.FLOWAPI.SourcingImportsRetrieve(ctx, "{import_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/imports/%7Bimport_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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition_import",
"livemode": false,
"mocked": true,
"status": "validating",
"validation_report": {
"entry_count": 0,
"error_count": 0,
"warning_count": 0,
"entries": [
{
"index": 1,
"severity": "error",
"code": "invalid_money_amount",
"detail": "The payee has no verified destination, so this line cannot be paid."
}
]
},
"requisition_ids": [
"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
]
}
/v1/sourcing/taxonomies/{taxonomy_id}#Read an occupation taxonomy
sourcing.taxonomies.retrieve
An occupation taxonomy is the set of codes a job title can resolve to. Reading one back is how your product shows a recruiter the code their title landed on, and which other titles would have reached the same one.
Send the taxonomy's id in the path. You get its version and one entry per occupation, each with the code, the canonical title and the local titles that resolve to it.
Store the version beside any code you keep. A title resolves within one version and not across them, so a code kept without its version cannot be reproduced later.
Path parameters
taxonomy_id
string
requiredThe taxonomy's identifier, starting with flow_enterprise_requisition_: the id on the taxonomy at GET /v1/sourcing/taxonomies/{taxonomy_id}, where your requisitions' occupation codes, such as ng-7412, come from. No call lists taxonomies.
Returns
The occupation codes with the titles that resolve to each, and the version you read.
id
string
requiredThe taxonomy's identifier, starting with flow_enterprise_requisition_; it never changes. Pass it as taxonomy_id at GET /v1/sourcing/taxonomies/{taxonomy_id} to read this taxonomy again; version says which version of it you are reading.
object
always "occupation_taxonomy"
requiredAlways occupation_taxonomy. Tells you which kind of record you are looking at, so one handler can read any response.
version
string
requiredWhich version of the taxonomy this is, such as 2026.08.1. What a title means changes between versions, and your requisition keeps meaning what it meant.
entries
array of object
requiredOne entry per occupation: its code, such as ng-7412, the canonical title, and the local and colloquial titles that mean the same thing. This is how a job title becomes an occupation code.
3 fields
occupation_code
string
requiredThe occupation's code, such as ng-7412. Send it as occupation_code on a requisition and you reach the people who know the role by any title in aliases.
canonical_title
string
requiredThe one standard title for this occupation, which every alias resolves to. Use it where you need a single name for a role candidates call many things.
aliases
array of string
requiredLocal and colloquial titles that mean this occupation.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingTaxonomiesRetrieve({ taxonomyId: '{taxonomy_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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.FLOWApi(client)
result = api.sourcing_taxonomies_retrieve(taxonomy_id='{taxonomy_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingTaxonomiesRetrieve(taxonomy_id: '{taxonomy_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingTaxonomiesRetrieve("{taxonomy_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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 FLOWApi(config);
var result = api.SourcingTaxonomiesRetrieve(taxonomyId: "{taxonomy_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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.FLOWAPI.SourcingTaxonomiesRetrieve(ctx, "{taxonomy_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/taxonomies/%7Btaxonomy_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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "occupation_taxonomy",
"version": "2026.08.1",
"entries": [
{
"occupation_code": "ng-7412",
"canonical_title": "example",
"aliases": [
"example"
]
}
]
}
/v1/sourcing/channels#Choose where a vacancy goes
sourcing.channels.list
A channel is one place a requisition can be posted, and this is the set your organisation can post to. So your product can offer a recruiter the choice of where a vacancy goes, with what each one costs and what is left of today's cap beside it.
Send nothing and every category comes back, paid and free together. category and paid narrow it. Each entry is one channel, and its id is the part you keep.
A channel's id begins flow_enterprise_broadcast_, the same prefix a broadcast carries. Keep the two apart by where you stored them; do not read the kind of record off the prefix.
Name an id in channel_ids at POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or read one channel on its own at GET /v1/sourcing/channels/{channel_id}.
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.
category
string
optionalOnly channels of one category: formal_board, professional_network, alumni_portal, campus_node or talent_pool (the Droomwork talent pool); the category, never the operator behind it. Leave it out to get every category.
formal_boardprofessional_networkalumni_portalcampus_nodetalent_poolpaid
boolean
optionalPass true to list only channels that charge and count against your spend ceilings, false to list only free ones. Leave it out to get both.
Returns
The channels you can post to, each with its cost model and today's remaining cap.
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 Channel
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
10 fields of Channel
id
string
requiredThe channel's identifier, starting with flow_enterprise_broadcast_; it never changes. You get it from GET /v1/sourcing/channels, and it is what you pass as channel_id and name in channel_ids when you broadcast.
object
always "channel"
requiredAlways channel. Tells you which kind of record you are looking at, so one handler can read any response.
category
string
requiredThe category, not the operator. Which platforms sit behind a category is not disclosed.
formal_boardprofessional_networkalumni_portalcampus_nodetalent_poollabel
string
optionalA name for your own reporting. Not the operator behind the channel.
paid
boolean
requiredtrue when posting here is charged and counts against your spend ceilings, false when it is free. cost_model says how it charges.
cost_model
object
optionalHow this channel charges, so a spend ceiling can be reasoned about.
2 fields
basis
string
optionalWhat you are charged for: per_post (each posting), per_click (each click), per_qualified_lead (each lead that passes your knockout rules) or free on a channel that never charges. indicative_unit_cost is roughly what one costs.
per_postper_clickper_qualified_leadfreeindicative_unit_cost
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.
quota
object
requiredHow many posts this channel takes from you in a day, and how many of those are left today. Either figure is null where no daily cap applies.
2 fields
posts_per_day
integer · nullable
optionalThe most posts this channel accepts from you in one day. null where no daily cap applies.
remaining_today
integer · nullable
optionalHow many posts you can still make on this channel today, counting down from posts_per_day. null where no daily cap applies.
rolling_yield
object
optionalYour own yield on this channel. Never anyone else's.
2 fields
qualified_lead_rate
number · nullable
optionalThe share of leads from this channel that passed your knockout rules over the last window_days days. Your own figure only, and null when there is nothing to measure yet.
window_days
integer
optionalHow many days the rolling yield is measured over, counted back from today. qualified_lead_rate covers exactly this window.
credentials_held
boolean
optionaltrue when credentials for posting on this channel are on file for your organisation. Without them nothing can post here, and GET /v1/sourcing/readiness lists the channels missing them.
available
boolean
optionaltrue when a broadcast can go to this channel right now, false when it can't. Read it before you name the channel in channel_ids on a broadcast.
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: limit (optional), category (optional), paid (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: limit (optional), category (optional), paid (optional)
const result = await api.sourcingChannelsList({ limit: 25, category: 'formal_board', paid: true });// query parameters: limit (optional), category (optional), paid (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true', {
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.FLOWApi(client)
# query parameters: limit (optional), category (optional), paid (optional)
result = api.sourcing_channels_list(limit=25, category='formal_board', paid=true)import os
import requests
# query parameters: limit (optional), category (optional), paid (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true',
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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional), category (optional), paid (optional)
$result = $api->sourcingChannelsList(limit: 25, category: 'formal_board', paid: true);<?php
// query parameters: limit (optional), category (optional), paid (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true');
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.FlowApi;
import com.droomwork.sdk.model.*;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: limit (optional), category (optional), paid (optional)
var result = api.sourcingChannelsList(25, FlowChannelCategory.fromValue("formal_board"), true);// query parameters: limit (optional), category (optional), paid (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true"))
.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 FLOWApi(config);
// query parameters: limit (optional), category (optional), paid (optional)
var result = api.SourcingChannelsList(limit: 25, category: FlowChannelCategory.FormalBoard, paid: true);// query parameters: limit (optional), category (optional), paid (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true");
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), category (optional), paid (optional)
result, _, err := client.FLOWAPI.SourcingChannelsList(ctx).Limit(25).Category(droomwork.FlowChannelCategory("formal_board")).Paid(true).Execute()// query parameters: limit (optional), category (optional), paid (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/channels?limit=25&category=formal_board&paid=true", 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": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel",
"category": "formal_board",
"paid": true,
"quota": {
"posts_per_day": 1,
"remaining_today": 1
},
"label": "Formal board A",
"cost_model": {
"basis": "per_post",
"indicative_unit_cost": {
"amount": 500000,
"currency": "NGN"
}
},
"rolling_yield": {
"qualified_lead_rate": 1,
"window_days": 1
},
"credentials_held": true,
"available": true
}
],
"has_more": true
}
/v1/sourcing/channels/{channel_id}#Check a channel before you send to it
sourcing.channels.retrieve
A channel is somewhere a requisition can be advertised. Reading one back is how your product decides whether to name it in a broadcast at all, before the send instead of after it has failed.
Send the channel's id in the path. You get the channel with what it charges, how much of today's quota is left, and your own yield on it.
Read available before you name a channel in a broadcast. credentials_held says whether a false answer is because your organisation has nothing on file for posting there.
Path parameters
channel_id
string
requiredThe channel's identifier, starting with flow_enterprise_broadcast_: the id of a channel listed at GET /v1/sourcing/channels, and the channel_id you see on artefacts, deliveries and leads.
Returns
One channel, to check what it charges and whether a broadcast can go to it now.
id
string
requiredThe channel's identifier, starting with flow_enterprise_broadcast_; it never changes. You get it from GET /v1/sourcing/channels, and it is what you pass as channel_id and name in channel_ids when you broadcast.
object
always "channel"
requiredAlways channel. Tells you which kind of record you are looking at, so one handler can read any response.
category
string
requiredThe category, not the operator. Which platforms sit behind a category is not disclosed.
formal_boardprofessional_networkalumni_portalcampus_nodetalent_poollabel
string
optionalA name for your own reporting. Not the operator behind the channel.
paid
boolean
requiredtrue when posting here is charged and counts against your spend ceilings, false when it is free. cost_model says how it charges.
cost_model
object
optionalHow this channel charges, so a spend ceiling can be reasoned about.
2 fields
basis
string
optionalWhat you are charged for: per_post (each posting), per_click (each click), per_qualified_lead (each lead that passes your knockout rules) or free on a channel that never charges. indicative_unit_cost is roughly what one costs.
per_postper_clickper_qualified_leadfreeindicative_unit_cost
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.
quota
object
requiredHow many posts this channel takes from you in a day, and how many of those are left today. Either figure is null where no daily cap applies.
2 fields
posts_per_day
integer · nullable
optionalThe most posts this channel accepts from you in one day. null where no daily cap applies.
remaining_today
integer · nullable
optionalHow many posts you can still make on this channel today, counting down from posts_per_day. null where no daily cap applies.
rolling_yield
object
optionalYour own yield on this channel. Never anyone else's.
2 fields
qualified_lead_rate
number · nullable
optionalThe share of leads from this channel that passed your knockout rules over the last window_days days. Your own figure only, and null when there is nothing to measure yet.
window_days
integer
optionalHow many days the rolling yield is measured over, counted back from today. qualified_lead_rate covers exactly this window.
credentials_held
boolean
optionaltrue when credentials for posting on this channel are on file for your organisation. Without them nothing can post here, and GET /v1/sourcing/readiness lists the channels missing them.
available
boolean
optionaltrue when a broadcast can go to this channel right now, false when it can't. Read it before you name the channel in channel_ids on a broadcast.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingChannelsRetrieve({ channelId: '{channel_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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.FLOWApi(client)
result = api.sourcing_channels_retrieve(channel_id='{channel_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingChannelsRetrieve(channel_id: '{channel_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingChannelsRetrieve("{channel_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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 FLOWApi(config);
var result = api.SourcingChannelsRetrieve(channelId: "{channel_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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.FLOWAPI.SourcingChannelsRetrieve(ctx, "{channel_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/channels/%7Bchannel_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": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel",
"category": "formal_board",
"paid": true,
"quota": {
"posts_per_day": 1,
"remaining_today": 1
},
"label": "Formal board A",
"cost_model": {
"basis": "per_post",
"indicative_unit_cost": {
"amount": 500000,
"currency": "NGN"
}
},
"rolling_yield": {
"qualified_lead_rate": 1,
"window_days": 1
},
"credentials_held": true,
"available": true
}
/v1/sourcing/requisitions/{requisition_id}/artefacts#Read the adverts before they go out
sourcing.artefacts.list
An artefact is the advert written for one channel. This lists the ones drafted for a requisition, so a recruiter reads what will go out channel by channel while there is still time to change it.
Send the requisition's id in the path. Each artefact comes back with the channel_id it was written for, its headline and body, and the fact_check comparing it against the requisition.
Read fact_check before you broadcast. It is where a claim in the copy that the requisition does not support is named, and it is the one thing on this list that can stop a send being worth making.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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.
Returns
The adverts for this requisition, each with its comparison result, ready for review before you broadcast.
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 Artefact
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
7 fields of Artefact
id
string
requiredThe artefact's identifier; it never changes. You get it from GET /v1/sourcing/requisitions/{requisition_id}/artefacts and pass it as artefact_id to retrieve or edit this copy under the same requisition.
object
always "channel_artefact"
requiredAlways channel_artefact. Tells you which kind of record you are looking at, so one handler can read any response.
channel_id
string
requiredThe channel this copy was written for: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Each artefact is sized and shaped for one destination, and a broadcast to that channel sends this copy.
headline
string
optionalThe title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.
body
string
requiredThe text of the advert as it will go out on this channel. Every pay figure, employer legal name, statutory claim and requirement in it is compared with your requisition; see fact_check.
fact_check
FactCheck
requiredThe result of comparing this copy against the requisition. Pay figures, employer legal names, statutory claims and requirements must match exactly. A mismatch fails the render and is logged rather than corrected quietly.
3 fields of FactCheck
passed
boolean
requiredtrue when every claim in checks agrees with the requisition. false when any one differs, and that copy does not reach the channel.
checks
array of object
requiredOne entry per claim compared, giving what the requisition says, what the copy says and whether they agree. Read it to see exactly which claim failed.
4 fields
claim
string
requiredWhich kind of fact was compared: pay_figure, employer_legal_name, statutory_claim (a claim about what the law provides) or requirement. All four must match the requisition exactly.
pay_figureemployer_legal_namestatutory_claimrequirementin_requisition
string
requiredThe value as your requisition states it. This is the side that wins: the copy has to say this, never the other way round.
in_content
string
requiredThe value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.
agrees
boolean
requiredtrue when in_content matches in_requisition exactly. One false here fails the whole check, and the copy with it.
failed_at
string · date-time · nullable
optionalWhen this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.
editable
boolean
optionalFalse once the broadcast has gone out on this channel.
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: limit (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?limit=25" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: limit (optional)
const result = await api.sourcingArtefactsList({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit: 25 });// query parameters: limit (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?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.FLOWApi(client)
# query parameters: limit (optional)
result = api.sourcing_artefacts_list(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit=25)import os
import requests
# query parameters: limit (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional)
$result = $api->sourcingArtefactsList(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', limit: 25);<?php
// query parameters: limit (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: limit (optional)
var result = api.sourcingArtefactsList("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 25);// query parameters: limit (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?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 FLOWApi(config);
// query parameters: limit (optional)
var result = api.SourcingArtefactsList(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", limit: 25);// query parameters: limit (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();import droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
// query parameters: limit (optional)
result, _, err := client.FLOWAPI.SourcingArtefactsList(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Limit(25).Execute()// query parameters: limit (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts?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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_artefact",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"body": "example",
"fact_check": {
"passed": true,
"checks": [
{
"claim": "pay_figure",
"in_requisition": "example",
"in_content": "example",
"agrees": true
}
],
"failed_at": "2026-09-01T09:00:00Z"
},
"headline": "example",
"editable": true
}
],
"has_more": true
}
/v1/sourcing/requisitions/{requisition_id}/artefacts/{artefact_id}#Read one channel's advert in full
sourcing.artefacts.retrieve
This reads one channel's advert back as it stands now. So a page in your product can refresh an advert it saved earlier, instead of showing wording that a later edit has replaced.
Send requisition_id and artefact_id in the path. You get that one artefact in full, with its headline, body and fact_check, and editable saying whether the wording can still change.
Change the wording at PATCH /v1/sourcing/requisitions/{requisition_id}/artefacts/{artefact_id}. An identifier in the path that is not there answers not_found.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
artefact_id
string
requiredThe artefact's identifier: the id of an artefact listed at GET /v1/sourcing/requisitions/{requisition_id}/artefacts, under the same requisition you name in this path.
Returns
One channel's advert as it now stands, with the comparison that was run on it.
id
string
requiredThe artefact's identifier; it never changes. You get it from GET /v1/sourcing/requisitions/{requisition_id}/artefacts and pass it as artefact_id to retrieve or edit this copy under the same requisition.
object
always "channel_artefact"
requiredAlways channel_artefact. Tells you which kind of record you are looking at, so one handler can read any response.
channel_id
string
requiredThe channel this copy was written for: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Each artefact is sized and shaped for one destination, and a broadcast to that channel sends this copy.
headline
string
optionalThe title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.
body
string
requiredThe text of the advert as it will go out on this channel. Every pay figure, employer legal name, statutory claim and requirement in it is compared with your requisition; see fact_check.
fact_check
FactCheck
requiredThe result of comparing this copy against the requisition. Pay figures, employer legal names, statutory claims and requirements must match exactly. A mismatch fails the render and is logged rather than corrected quietly.
3 fields of FactCheck
passed
boolean
requiredtrue when every claim in checks agrees with the requisition. false when any one differs, and that copy does not reach the channel.
checks
array of object
requiredOne entry per claim compared, giving what the requisition says, what the copy says and whether they agree. Read it to see exactly which claim failed.
4 fields
claim
string
requiredWhich kind of fact was compared: pay_figure, employer_legal_name, statutory_claim (a claim about what the law provides) or requirement. All four must match the requisition exactly.
pay_figureemployer_legal_namestatutory_claimrequirementin_requisition
string
requiredThe value as your requisition states it. This is the side that wins: the copy has to say this, never the other way round.
in_content
string
requiredThe value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.
agrees
boolean
requiredtrue when in_content matches in_requisition exactly. One false here fails the whole check, and the copy with it.
failed_at
string · date-time · nullable
optionalWhen this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.
editable
boolean
optionalFalse once the broadcast has gone out on this channel.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingArtefactsRetrieve({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', artefactId: '{artefact_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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.FLOWApi(client)
result = api.sourcing_artefacts_retrieve(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', artefact_id='{artefact_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingArtefactsRetrieve(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', artefact_id: '{artefact_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingArtefactsRetrieve("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{artefact_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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 FLOWApi(config);
var result = api.SourcingArtefactsRetrieve(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", artefactId: "{artefact_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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.FLOWAPI.SourcingArtefactsRetrieve(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{artefact_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_artefact",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"body": "example",
"fact_check": {
"passed": true,
"checks": [
{
"claim": "pay_figure",
"in_requisition": "example",
"in_content": "example",
"agrees": true
}
],
"failed_at": "2026-09-01T09:00:00Z"
},
"headline": "example",
"editable": true
}
/v1/sourcing/requisitions/{requisition_id}/artefacts/{artefact_id}#Put your own words on an advert
sourcing.artefacts.update
The copy is generated, and this is where you make it yours. Send your own wording for one channel, so your product can let a hiring manager fix a line that reads badly on one destination and leave every other channel alone.
Send headline, body, or both. You get the whole artefact back, so one response is enough to redraw the advert on your own screen.
You need the artefact's id, which comes from GET /v1/sourcing/requisitions/{requisition_id}/artefacts.
Broadcast at POST /v1/sourcing/requisitions/{requisition_id}/broadcasts once the wording is what you want. An identifier in the path that is not there answers not_found.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
artefact_id
string
requiredThe artefact's identifier: the id of an artefact listed at GET /v1/sourcing/requisitions/{requisition_id}/artefacts, under the same requisition you name in this path.
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
headline
string
optionalThe new title line for this channel's copy; leave it out to keep the current one. It is checked against the requisition the same way generated copy is.
body
string
optionalThe new text of the advert for this channel; leave it out to keep the current one. A pay figure, employer legal name, statutory claim or requirement the requisition doesn't carry fails the check.
Returns
The advert as it now reads, so you can confirm the wording you sent was stored.
id
string
requiredThe artefact's identifier; it never changes. You get it from GET /v1/sourcing/requisitions/{requisition_id}/artefacts and pass it as artefact_id to retrieve or edit this copy under the same requisition.
object
always "channel_artefact"
requiredAlways channel_artefact. Tells you which kind of record you are looking at, so one handler can read any response.
channel_id
string
requiredThe channel this copy was written for: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Each artefact is sized and shaped for one destination, and a broadcast to that channel sends this copy.
headline
string
optionalThe title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.
body
string
requiredThe text of the advert as it will go out on this channel. Every pay figure, employer legal name, statutory claim and requirement in it is compared with your requisition; see fact_check.
fact_check
FactCheck
requiredThe result of comparing this copy against the requisition. Pay figures, employer legal names, statutory claims and requirements must match exactly. A mismatch fails the render and is logged rather than corrected quietly.
3 fields of FactCheck
passed
boolean
requiredtrue when every claim in checks agrees with the requisition. false when any one differs, and that copy does not reach the channel.
checks
array of object
requiredOne entry per claim compared, giving what the requisition says, what the copy says and whether they agree. Read it to see exactly which claim failed.
4 fields
claim
string
requiredWhich kind of fact was compared: pay_figure, employer_legal_name, statutory_claim (a claim about what the law provides) or requirement. All four must match the requisition exactly.
pay_figureemployer_legal_namestatutory_claimrequirementin_requisition
string
requiredThe value as your requisition states it. This is the side that wins: the copy has to say this, never the other way round.
in_content
string
requiredThe value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.
agrees
boolean
requiredtrue when in_content matches in_requisition exactly. One false here fails the whole check, and the copy with it.
failed_at
string · date-time · nullable
optionalWhen this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.
editable
boolean
optionalFalse once the broadcast has gone out on this channel.
Other responses
Errors it can return
curl -X PATCH "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"headline":"example","body":"example"}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingArtefactsUpdate({
requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
artefactId: '{artefact_id}',
idempotencyKey: crypto.randomUUID(),
flowArtefactUpdateRequest: {"headline":"example","body":"example"},
});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D', {
method: 'PATCH',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"headline": "example",
"body": "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.FLOWApi(client)
result = api.sourcing_artefacts_update(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', artefact_id='{artefact_id}', body={"headline": "example", "body": "example"})import os
import uuid
import requests
response = requests.request(
'PATCH',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"headline": "example", "body": "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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingArtefactsUpdate($idempotencyKey, json_decode('{"headline":"example","body":"example"}', true));<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => '{"headline":"example","body":"example"}',
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingArtefactsUpdate("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{artefact_id}", idempotencyKey, body);var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D"))
.header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
.header("Content-Type", "application/json")
.header("Idempotency-Key", UUID.randomUUID().toString())
.method("PATCH", HttpRequest.BodyPublishers.ofString("""
{
"headline": "example",
"body": "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 FLOWApi(config);
var result = api.SourcingArtefactsUpdate(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", artefactId: "{artefact_id}", idempotencyKey, body);using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Patch, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"headline": "example",
"body": "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.FLOWAPI.SourcingArtefactsUpdate(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{artefact_id}").IdempotencyKey(key).FlowArtefactUpdateRequest(body).Execute()body := strings.NewReader(`{
"headline": "example",
"body": "example"
}`)
req, _ := http.NewRequest("PATCH", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/artefacts/%7Bartefact_id%7D", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_artefact",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"body": "example",
"fact_check": {
"passed": true,
"checks": [
{
"claim": "pay_figure",
"in_requisition": "example",
"in_content": "example",
"agrees": true
}
],
"failed_at": "2026-09-01T09:00:00Z"
},
"headline": "example",
"editable": true
}
/v1/sourcing/requisitions/{requisition_id}/broadcasts#Put a requisition out on your channels
sourcing.broadcasts.create
A broadcast is one send of one requisition to a set of channels. This is the call that puts the vacancy in front of candidates, and it comes back with a record for each channel, so your product can follow them one at a time.
Name the channels in channel_ids, or send no request body. You get the broadcast's id and its deliveries, one for each channel.
The requisition in the path has to exist, from POST /v1/sourcing/requisitions.
Accepted is not posted. Each channel runs on its own, so read the deliveries before you tell anybody the vacancy is live.
Carry the id as broadcast_id to GET /v1/sourcing/broadcasts/{broadcast_id}.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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
channel_ids
array of string
optionalDefaults to every channel configured for your organisation.
Returns
The accepted broadcast, with one delivery per channel so you can follow each one.
id
string
requiredThe broadcast's identifier, starting with flow_enterprise_broadcast_; it never changes. You get it from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts and pass it as broadcast_id to read it, retry a delivery or filter deliveries.
object
always "broadcast"
requiredAlways broadcast. 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.
requisition_id
string
requiredThe requisition this broadcast sent out: its id, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. It is the one you named in the path when you created the broadcast, returned so the record stands on its own.
requisition_version
integer
requiredWhich version of the requisition went out: its version at the moment you broadcast. Leads from this broadcast stay bound to it, so you can reproduce a shortlist.
deliveries
array of Delivery
requiredOne delivery per channel the broadcast went to, each with its own state, attempt count and fault. A channel that failed is listed here as failed, never dropped.
10 fields of Delivery
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
started_at
string · date-time
optionalWhen the broadcast started running, as an RFC 3339 timestamp in UTC. Each channel then runs on its own, so read deliveries for how far each one got.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"channel_ids":["sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"]}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingBroadcastsCreate({
requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
idempotencyKey: crypto.randomUUID(),
flowBroadcastCreateRequest: {"channelIds":["sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"]},
});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"channel_ids": [
"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.FLOWApi(client)
result = api.sourcing_broadcasts_create(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"channel_ids": ["sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"]})import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"channel_ids": ["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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingBroadcastsCreate($idempotencyKey, json_decode('{"channel_ids":["sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"]}', true));<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts');
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 => '{"channel_ids":["sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"]}',
]);
$result = json_decode(curl_exec($ch), true);import com.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingBroadcastsCreate("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts"))
.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("""
{
"channel_ids": [
"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 FLOWApi(config);
var result = api.SourcingBroadcastsCreate(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"channel_ids": [
"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.FLOWAPI.SourcingBroadcastsCreate(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).FlowBroadcastCreateRequest(body).Execute()body := strings.NewReader(`{
"channel_ids": [
"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/broadcasts", 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": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "broadcast",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"deliveries": [
{
"id": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
],
"started_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/broadcasts/{broadcast_id}#See how far each channel got
sourcing.broadcasts.retrieve
A broadcast is one send of one requisition to several channels at once. Reading it back is how your product shows how far each channel got, because they do not finish together and one of them stalling says nothing about the rest.
Send the broadcast's id in the path. You get the broadcast with requisition_version, which is the version that went out, and one delivery for each channel it went to.
Each channel runs on its own, so read each delivery's state and attempts and never the broadcast as a single outcome.
A delivery you need tried again goes on its own at POST /v1/sourcing/broadcasts/{broadcast_id}/deliveries/{delivery_id}/retry.
Path parameters
broadcast_id
string
requiredThe broadcast's identifier, starting with flow_enterprise_broadcast_: the id returned by POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, also carried as broadcast_id on every delivery.
Returns
One send with every channel's delivery on it, for seeing how far each got.
id
string
requiredThe broadcast's identifier, starting with flow_enterprise_broadcast_; it never changes. You get it from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts and pass it as broadcast_id to read it, retry a delivery or filter deliveries.
object
always "broadcast"
requiredAlways broadcast. 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.
requisition_id
string
requiredThe requisition this broadcast sent out: its id, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. It is the one you named in the path when you created the broadcast, returned so the record stands on its own.
requisition_version
integer
requiredWhich version of the requisition went out: its version at the moment you broadcast. Leads from this broadcast stay bound to it, so you can reproduce a shortlist.
deliveries
array of Delivery
requiredOne delivery per channel the broadcast went to, each with its own state, attempt count and fault. A channel that failed is listed here as failed, never dropped.
10 fields of Delivery
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
started_at
string · date-time
optionalWhen the broadcast started running, as an RFC 3339 timestamp in UTC. Each channel then runs on its own, so read deliveries for how far each one got.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingBroadcastsRetrieve({ broadcastId: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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.FLOWApi(client)
result = api.sourcing_broadcasts_retrieve(broadcast_id='flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingBroadcastsRetrieve(broadcast_id: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingBroadcastsRetrieve("flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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 FLOWApi(config);
var result = api.SourcingBroadcastsRetrieve(broadcastId: "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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.FLOWAPI.SourcingBroadcastsRetrieve(ctx, "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_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": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "broadcast",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"deliveries": [
{
"id": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
],
"started_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/broadcasts/{broadcast_id}/deliveries/{delivery_id}/retry#Try one channel again on its own
sourcing.deliveries.retry
One channel going wrong is not the broadcast going wrong, and this is the call that acts on the difference. It puts one channel's delivery back in the queue and leaves every other channel on that send exactly as it was.
Send the broadcast's id and the delivery's id in the path, and no request body. You get the delivery back in queued.
The delivery is one of those listed on a broadcast at GET /v1/sourcing/broadcasts/{broadcast_id}.
Path parameters
broadcast_id
string
requiredThe broadcast's identifier, starting with flow_enterprise_broadcast_: the id returned by POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, also carried as broadcast_id on every delivery.
delivery_id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_: the id of an entry in deliveries on a broadcast, of a delivery listed at GET /v1/sourcing/deliveries, or of one you recorded at POST /v1/sourcing/deliveries.
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 delivery back in the queue for another attempt at that one channel.
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingDeliveriesRetry({ broadcastId: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', deliveryId: '{delivery_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry', {
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.FLOWApi(client)
result = api.sourcing_deliveries_retry(broadcast_id='flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', delivery_id='{delivery_id}')import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingDeliveriesRetry(broadcast_id: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', delivery_id: '{delivery_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingDeliveriesRetry("flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{delivery_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry"))
.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 FLOWApi(config);
var result = api.SourcingDeliveriesRetry(broadcastId: "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", deliveryId: "{delivery_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry");
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.FLOWAPI.SourcingDeliveriesRetry(ctx, "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "{delivery_id}").Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/broadcasts/flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/deliveries/%7Bdelivery_id%7D/retry", 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": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
/v1/sourcing/deliveries#See which channels a vacancy reached
sourcing.deliveries.list
A delivery is one channel's record of one broadcast. This is every delivery your organisation holds, so a screen shows which channels a vacancy actually reached without opening each broadcast in turn.
Send nothing and the deliveries come back, each naming the broadcast and the channel it belongs to, the state it reached and how many attempts have been made.
Each channel runs on its own, so a delivery is read on its own and never as the broadcast's single outcome.
Try one again at POST /v1/sourcing/broadcasts/{broadcast_id}/deliveries/{delivery_id}/retry, passing that row's broadcast_id and its own id.
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.
broadcast_id
string
optionalOne broadcast's deliveries, one per channel it went to: pass the broadcast's id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts. Leave it out to get deliveries from every broadcast.
requisition_id
string
optionalOnly deliveries for one requisition, across every broadcast of it: pass the requisition's id, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Leave it out to get deliveries for every requisition.
state
string
optionalRestricts the list to one state: queued (not yet tried), sending, delivered, retrying (failed, another try is scheduled) or failed (terminal, the channel was never reached). Leave it out to get every delivery, whatever its state.
queuedsendingdeliveredretryingfailedReturns
One record per channel per send, each with its own state and attempt count.
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 Delivery
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
10 fields of Delivery
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
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: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
const result = await api.sourcingDeliveriesList({ limit: 25, broadcastId: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', state: 'queued' });// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued', {
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.FLOWApi(client)
# query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
result = api.sourcing_deliveries_list(limit=25, broadcast_id='flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', state='queued')import os
import requests
# query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued',
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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
$result = $api->sourcingDeliveriesList(limit: 25, broadcast_id: 'flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', state: 'queued');<?php
// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued');
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.FlowApi;
import com.droomwork.sdk.model.*;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
var result = api.sourcingDeliveriesList(25, null, "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", FlowDeliveryState.fromValue("queued"));// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued"))
.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 FLOWApi(config);
// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
var result = api.SourcingDeliveriesList(limit: 25, broadcastId: "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", state: FlowDeliveryState.Queued);// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued");
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), broadcast_id (optional), requisition_id (optional), state (optional)
result, _, err := client.FLOWAPI.SourcingDeliveriesList(ctx).Limit(25).BroadcastId("flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").RequisitionId("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").State(droomwork.FlowDeliveryState("queued")).Execute()// query parameters: limit (optional), starting_after (optional), broadcast_id (optional), requisition_id (optional), state (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/deliveries?limit=25&broadcast_id=flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&state=queued", 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": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
],
"has_more": true
}
/v1/sourcing/deliveries#Log a posting you made by hand
sourcing.deliveries.create
Not every channel is one we can post to. When you placed the advert there yourself, record it here, and that posting becomes a delivery like the ones a broadcast makes. So your record of where a vacancy went covers everywhere it went.
The body takes broadcast_id and channel_id, and neither is required. You get the whole delivery back, with an id of its own starting flow_enterprise_delivery_, and that is the identifier to keep.
The broadcast has to exist, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, and the channel has to be one of yours, from GET /v1/sourcing/channels.
Send both fields on every call. The schema requires neither, and a delivery naming the wrong broadcast is a record you cannot trace.
Read it back at GET /v1/sourcing/deliveries/{delivery_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 optional
broadcast_id
string
optionalThe broadcast this delivery belongs to: the id of a broadcast you created at POST /v1/sourcing/requisitions/{requisition_id}/broadcasts or read at GET /v1/sourcing/broadcasts/{broadcast_id}. It starts with flow_enterprise_broadcast_.
channel_id
string
optionalThe channel you posted on by hand: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. The posting then counts in attribution like every other delivery.
Returns
The posting you recorded, with the identifier to read it back by.
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/deliveries" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"broadcast_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","channel_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingDeliveriesCreate({});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/deliveries', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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.FLOWApi(client)
result = api.sourcing_deliveries_create()import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/deliveries',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "channel_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingDeliveriesCreate();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/deliveries');
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 => '{"broadcast_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","channel_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingDeliveriesCreate();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/deliveries"))
.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("""
{
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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 FLOWApi(config);
var result = api.SourcingDeliveriesCreate();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/deliveries");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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.FLOWAPI.SourcingDeliveriesCreate(ctx).Execute()body := strings.NewReader(`{
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/deliveries", 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": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
/v1/sourcing/spend_ceilings#What is left to spend on channels
sourcing.spend_ceilings.retrieve
Posting on a paid channel spends against a ceiling, and this is where you read how much of it is gone. So your product can put the headroom in front of a recruiter before they pick paid channels, instead of leaving them to add up unit costs.
You send nothing, and there is no identifier to pass: there is one of these for your organisation. You get the organisation's ceiling with what is consumed against it, and the same pair for each requisition that has a ceiling of its own.
A requisition with no entry in per_requisition has no ceiling of its own. Where it has one, the smaller headroom is the figure to plan against.
Read which channels charge at GET /v1/sourcing/channels, then send at POST /v1/sourcing/requisitions/{requisition_id}/broadcasts.
Returns
Your organisation's ceiling and each requisition's own, with what is consumed against them.
object
always "spend_ceilings"
requiredAlways spend_ceilings. Tells you which kind of record you are looking at, so one handler can read any response.
organisation
object
requiredYour organisation's ceiling on paid channel spend across every requisition, and what you've consumed against it. Checked before any paid channel runs.
2 fields
ceiling
Money
required2 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.
consumed
Money
required2 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.
per_requisition
array of object
optionalOne entry per requisition with its own ceiling, and what's been consumed against it. A broadcast is checked against the requisition's ceiling and your organisation's, and either can block it.
3 fields
requisition_id
string
requiredThe requisition this ceiling applies to: the id of one of your requisitions, from POST /v1/sourcing/requisitions or GET /v1/sourcing/requisitions. It starts with flow_enterprise_requisition_.
ceiling
Money
required2 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.
consumed
Money
required2 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.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/spend_ceilings" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingSpendCeilingsRetrieve({});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/spend_ceilings', {
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.FLOWApi(client)
result = api.sourcing_spend_ceilings_retrieve()import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/spend_ceilings',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingSpendCeilingsRetrieve();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/spend_ceilings');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingSpendCeilingsRetrieve();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/spend_ceilings"))
.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 FLOWApi(config);
var result = api.SourcingSpendCeilingsRetrieve();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/spend_ceilings");
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.FLOWAPI.SourcingSpendCeilingsRetrieve(ctx).Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/spend_ceilings", 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": "spend_ceilings",
"organisation": {
"ceiling": {
"amount": 100000000,
"currency": "NGN"
},
"consumed": {
"amount": 52500000,
"currency": "NGN"
}
},
"per_requisition": [
{
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"ceiling": {
"amount": 100000000,
"currency": "NGN"
},
"consumed": {
"amount": 52500000,
"currency": "NGN"
}
}
]
}
/v1/sourcing/requisitions/{requisition_id}/capture_links#Give one channel its own link
sourcing.capture_links.create
This makes the link candidates follow to answer one requisition, bound to the one channel you name. So you can hand a channel one link and have your knockout questions asked on it, instead of building a page of your own.
Send the requisition's id in the path and the channel's id in the body. You get back the url to hand out and the token inside it.
The requisition has to exist first, from POST /v1/sourcing/requisitions, and the channel has to be one from GET /v1/sourcing/channels.
A capture link's id starts with flow_enterprise_lead_, which is also a lead's prefix. Keep the two kinds apart in your own storage, because the prefix will not.
Create one link per channel; no call moves a link to another channel afterwards. Carry the id as capture_link_id to check the link at GET /v1/sourcing/capture_links/{capture_link_id}.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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
channel_id
string
requiredThe channel this link is for: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Every lead who follows the link is attributed to this channel.
expires_at
string · date-time
optionalWhen the link stops working, as an RFC 3339 timestamp in UTC. Omit it for a link with no expiry.
Returns
The link to hand out, with the channel it is bound to and its opaque token.
id
string
requiredThe capture link's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as capture_link_id at GET /v1/sourcing/capture_links/{capture_link_id}, and read it back on a lead's attribution.
object
always "capture_link"
requiredAlways capture_link. Tells you which kind of record you are looking at, so one handler can read any response.
requisition_id
string
requiredThe requisition the link captures leads for: the requisition_id you put in the path at POST /v1/sourcing/requisitions/{requisition_id}/capture_links, that requisition's id starting with flow_enterprise_requisition_.
channel_id
string
optionalThe channel the link was created for: the channel_id you sent at POST /v1/sourcing/requisitions/{requisition_id}/capture_links, starting with flow_enterprise_broadcast_. Every lead who follows the link is attributed to it.
url
string · uri
requiredCarries the token and nothing else. No name, no phone number and no identifier ever appears in a query parameter, because a link gets pasted where you cannot see it.
token
string
requiredOpaque and not enumerable. Guessing a neighbouring one finds nothing.
leads_captured
integer · minimum 0
optionalHow many leads have come in through this link so far. 0 for a link nobody has followed yet.
expires_at
string · date-time · nullable
optionalWhen the link stops working, as an RFC 3339 timestamp in UTC. null when the link has no expiry.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"channel_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_at":"2026-09-01T09:00:00Z"}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingCaptureLinksCreate({
requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
idempotencyKey: crypto.randomUUID(),
flowCaptureLinkCreateRequest: {"channelId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expiresAt":"2026-09-01T09:00:00Z"},
});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"expires_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.FLOWApi(client)
result = api.sourcing_capture_links_create(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', body={"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "expires_at": "2026-09-01T09:00:00Z"})import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "expires_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingCaptureLinksCreate($idempotencyKey, json_decode('{"channel_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_at":"2026-09-01T09:00:00Z"}', true));<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links');
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 => '{"channel_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingCaptureLinksCreate("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links"))
.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("""
{
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"expires_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 FLOWApi(config);
var result = api.SourcingCaptureLinksCreate(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", idempotencyKey, body);using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"expires_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.FLOWAPI.SourcingCaptureLinksCreate(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").IdempotencyKey(key).FlowCaptureLinkCreateRequest(body).Execute()body := strings.NewReader(`{
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"expires_at": "2026-09-01T09:00:00Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/capture_links", 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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "capture_link",
"url": "https://files.sandbox.droomwork.com/example",
"token": "example",
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"leads_captured": 0,
"expires_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/capture_links/{capture_link_id}#Check one link and what it brought in
sourcing.capture_links.retrieve
A capture link is the address a candidate follows to put themselves forward for one requisition. Reading it back is how your product shows what a link has brought in, without keeping a count of its own beside it.
Send the link's id in the path. You get the link with its url, the requisition it captures for, how many leads have come through it so far, and when it stops working.
expires_at is null on a link that has no expiry, so check for that before you treat a date as the one it runs out on.
Path parameters
capture_link_id
string
requiredThe capture link's identifier: the id in the response to POST /v1/sourcing/requisitions/{requisition_id}/capture_links, or the capture_link_id on a lead's attribution. It starts with flow_enterprise_lead_.
Returns
The link as it stands, with the number of leads captured through it so far.
id
string
requiredThe capture link's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as capture_link_id at GET /v1/sourcing/capture_links/{capture_link_id}, and read it back on a lead's attribution.
object
always "capture_link"
requiredAlways capture_link. Tells you which kind of record you are looking at, so one handler can read any response.
requisition_id
string
requiredThe requisition the link captures leads for: the requisition_id you put in the path at POST /v1/sourcing/requisitions/{requisition_id}/capture_links, that requisition's id starting with flow_enterprise_requisition_.
channel_id
string
optionalThe channel the link was created for: the channel_id you sent at POST /v1/sourcing/requisitions/{requisition_id}/capture_links, starting with flow_enterprise_broadcast_. Every lead who follows the link is attributed to it.
url
string · uri
requiredCarries the token and nothing else. No name, no phone number and no identifier ever appears in a query parameter, because a link gets pasted where you cannot see it.
token
string
requiredOpaque and not enumerable. Guessing a neighbouring one finds nothing.
leads_captured
integer · minimum 0
optionalHow many leads have come in through this link so far. 0 for a link nobody has followed yet.
expires_at
string · date-time · nullable
optionalWhen the link stops working, as an RFC 3339 timestamp in UTC. null when the link has no expiry.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingCaptureLinksRetrieve({ captureLinkId: '{capture_link_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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.FLOWApi(client)
result = api.sourcing_capture_links_retrieve(capture_link_id='{capture_link_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingCaptureLinksRetrieve(capture_link_id: '{capture_link_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingCaptureLinksRetrieve("{capture_link_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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 FLOWApi(config);
var result = api.SourcingCaptureLinksRetrieve(captureLinkId: "{capture_link_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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.FLOWAPI.SourcingCaptureLinksRetrieve(ctx, "{capture_link_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/capture_links/%7Bcapture_link_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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "capture_link",
"url": "https://files.sandbox.droomwork.com/example",
"token": "example",
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"leads_captured": 0,
"expires_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/requisitions/{requisition_id}/knockout_rules#See what a requisition screens on
sourcing.knockout_rules.list
A knockout rule is a question a candidate is asked together with the test their answer is judged by. This lists the ones a requisition screens on, so a recruiter can see what is being asked of people before anybody answers it.
Send the requisition's id in the path. Each rule comes back with its code, the question as it is put, the rule the answer is tested against, and the rule_pack_version that judges it.
What one candidate answered is read against their lead, at GET /v1/sourcing/leads/{lead_id}/screening.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
Returns
Every rule this requisition screens on, with the rule pack version that will judge each.
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 KnockoutRule
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
5 fields of KnockoutRule
code
string
requiredA short code that names this rule. Use it in your own reporting to refer to the rule without quoting the question or the test.
question
string
requiredWhat the candidate is asked. The conversation may phrase it differently, but the answer is judged by rule, never by the phrasing.
rule
string
requiredThe deterministic test applied to what was extracted from the answer, such as years_experience >= 3. It is the only thing that decides pass or fail.
rule_pack_version
string
requiredThe version of the rule pack this rule belongs to, such as 2026.08.1. Rules are versioned, so you can reproduce an outcome against the version that decided it.
mandatory
boolean
optionaltrue when every candidate for this requisition is asked this question and judged by this rule. false when the conversation may leave it out.
question_cap
integer · defaults to 5
requiredFive by default. A screening conversation that runs longer than this stops being screening and starts being an interview.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingKnockoutRulesList({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules', {
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.FLOWApi(client)
result = api.sourcing_knockout_rules_list(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingKnockoutRulesList(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingKnockoutRulesList("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules"))
.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 FLOWApi(config);
var result = api.SourcingKnockoutRulesList(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules");
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.FLOWAPI.SourcingKnockoutRulesList(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/knockout_rules", 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": [
{
"code": "invalid_money_amount",
"question": "example",
"rule": "years_experience >= 3",
"rule_pack_version": "2026.08.1",
"mandatory": true
}
],
"question_cap": 1
}
/v1/sourcing/leads#See who has answered your requisitions
sourcing.leads.list
A lead is one candidate's answer to one of your requisitions, and this gives you the ones your organisation holds. Each row is the whole record, the fit score included, so you can rank a review queue in your product from one call.
Send nothing to get every lead you hold, or narrow it by requisition_id to one vacancy and by outcome to one stage of screening.
A lead can be on this list before your rules have decided about it. Read screening_outcome on each row, and read it again before you act on a shortlist you saved earlier.
Carry a row's id as lead_id to open that candidate at GET /v1/sourcing/leads/{lead_id}.
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.
requisition_id
string
optionalRestricts the list to the leads of one requisition: its id from POST /v1/sourcing/requisitions or GET /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. Leave it out to get leads across all your requisitions.
outcome
string
optionalRestricts the list to one screening outcome: in_progress (still answering), passed or failed (your knockout rules decided), or abandoned (stopped before the end). Pass passed for your shortlist; leave it out to get every lead.
in_progresspassedfailedabandonedReturns
A page of whole lead records, enough to fill a review queue without opening each one.
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 Lead
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
14 fields of Lead
id
string
requiredThe lead's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as lead_id on GET /v1/sourcing/leads/{lead_id} and the /screening, /attribution and /promote routes under it.
object
always "lead"
requiredAlways lead. 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.
requisition_id
string
requiredThe requisition this lead answered: its id from POST /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. With requisition_version, it pins the exact version the lead was captured against.
requisition_version
integer
requiredWhich version produced this lead. A shortlist is reproducible against it.
channel_id
string
optionalThe channel the lead came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. It is what your yield at GET /v1/sourcing/analytics counts against.
screening_outcome
string
optionalin_progresspassedfailedabandonedfit_score
number · minimum 0 · maximum 100
requiredHow well the lead fits the requisition, from 0 to 100. The three factors behind it are in top_factors, so a score never arrives without a reason.
top_factors
array of object
optionalThe three factors that most influenced the score. Three, always, so a score is never a number without a reason.
2 fields
factor
string
requiredThe name of one of the three things that most influenced fit_score. Read it with contribution to see how far it moved the score.
contribution
number
requiredHow much this factor moved fit_score. Read the three together to see why the lead scored as it did.
consent
LeadConsent
requiredCaptured at first contact, before anything else is asked.
4 fields of LeadConsent
lawful_basis
string
requiredThe lawful basis you process this candidate's data under: consent (they agreed), legitimate_interest (your hiring interest) or contract (steps toward a contract with them). Recorded at first contact, before anything else is asked.
consentlegitimate_interestcontractchannel
string
requiredThe channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.
wording_version
string
requiredThe exact wording presented. Answered from the record, not the current page.
captured_at
string · date-time
requiredWhen the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.
duplicate_of
string · nullable
optionalSet when this response matched an existing lead on contact identity.
promoted_subject_ref
string · nullable
optionalSet once you promote the lead at POST /v1/sourcing/leads/{lead_id}/promote, null before: the person's opaque reference, starting with sub_, that you send as subject_ref at POST /v1/identity/consent_tokens and POST /v1/identity/verifications.
captured_at
string · date-time
optionalWhen the lead came in, as an RFC 3339 timestamp in UTC. Screening can still be in_progress after this; it says when the candidate was captured, not when they were judged.
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: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
const result = await api.sourcingLeadsList({ limit: 25, requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'in_progress' });// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress', {
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.FLOWApi(client)
# query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
result = api.sourcing_leads_list(limit=25, requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome='in_progress')import os
import requests
# query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress',
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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
$result = $api->sourcingLeadsList(limit: 25, requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'in_progress');<?php
// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress');
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.FlowApi;
import com.droomwork.sdk.model.*;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
var result = api.sourcingLeadsList(25, null, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", FlowScreeningOutcome.fromValue("in_progress"));// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress"))
.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 FLOWApi(config);
// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
var result = api.SourcingLeadsList(limit: 25, requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", outcome: FlowScreeningOutcome.InProgress);// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress");
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), requisition_id (optional), outcome (optional)
result, _, err := client.FLOWAPI.SourcingLeadsList(ctx).Limit(25).RequisitionId("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Outcome(droomwork.FlowScreeningOutcome("in_progress")).Execute()// query parameters: limit (optional), starting_after (optional), requisition_id (optional), outcome (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/leads?limit=25&requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=in_progress", 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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "lead",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"fit_score": 0,
"consent": {
"lawful_basis": "consent",
"channel": "example",
"wording_version": "2026.08.1",
"captured_at": "2026-09-01T09:00:00Z"
},
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"screening_outcome": "in_progress",
"top_factors": [
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
}
],
"duplicate_of": "example",
"promoted_subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"captured_at": "2026-09-01T09:00:00Z"
}
],
"has_more": true
}
/v1/sourcing/leads#Record a lead
sourcing.leads.create
This records a candidate you already know about against one of your requisitions. It comes back scored, and the same reads work on it as on a lead that came in by itself, so one review queue in your product can hold both.
Send the requisition the candidate answered and the channel they came through. You get the lead back with its own id.
The requisition has to exist first, from POST /v1/sourcing/requisitions, and the channel has to be one from GET /v1/sourcing/channels.
Both fields are optional in the schema, and a lead is created without them. Send both: no later call changes the requisition or the channel on a lead.
Carry the id as lead_id to read the screening behind the outcome at GET /v1/sourcing/leads/{lead_id}/screening.
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
requisition_id
string
optionalThe requisition the lead is for: the id of a requisition you created at POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions. It starts with flow_enterprise_requisition_.
channel_id
string
optionalThe channel the lead came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Your yield at GET /v1/sourcing/analytics counts the lead against it.
Returns
The lead as recorded, with the id every later call about this candidate uses.
id
string
requiredThe lead's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as lead_id on GET /v1/sourcing/leads/{lead_id} and the /screening, /attribution and /promote routes under it.
object
always "lead"
requiredAlways lead. 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.
requisition_id
string
requiredThe requisition this lead answered: its id from POST /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. With requisition_version, it pins the exact version the lead was captured against.
requisition_version
integer
requiredWhich version produced this lead. A shortlist is reproducible against it.
channel_id
string
optionalThe channel the lead came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. It is what your yield at GET /v1/sourcing/analytics counts against.
screening_outcome
string
optionalin_progresspassedfailedabandonedfit_score
number · minimum 0 · maximum 100
requiredHow well the lead fits the requisition, from 0 to 100. The three factors behind it are in top_factors, so a score never arrives without a reason.
top_factors
array of object
optionalThe three factors that most influenced the score. Three, always, so a score is never a number without a reason.
2 fields
factor
string
requiredThe name of one of the three things that most influenced fit_score. Read it with contribution to see how far it moved the score.
contribution
number
requiredHow much this factor moved fit_score. Read the three together to see why the lead scored as it did.
consent
LeadConsent
requiredCaptured at first contact, before anything else is asked.
4 fields of LeadConsent
lawful_basis
string
requiredThe lawful basis you process this candidate's data under: consent (they agreed), legitimate_interest (your hiring interest) or contract (steps toward a contract with them). Recorded at first contact, before anything else is asked.
consentlegitimate_interestcontractchannel
string
requiredThe channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.
wording_version
string
requiredThe exact wording presented. Answered from the record, not the current page.
captured_at
string · date-time
requiredWhen the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.
duplicate_of
string · nullable
optionalSet when this response matched an existing lead on contact identity.
promoted_subject_ref
string · nullable
optionalSet once you promote the lead at POST /v1/sourcing/leads/{lead_id}/promote, null before: the person's opaque reference, starting with sub_, that you send as subject_ref at POST /v1/identity/consent_tokens and POST /v1/identity/verifications.
captured_at
string · date-time
optionalWhen the lead came in, as an RFC 3339 timestamp in UTC. Screening can still be in_progress after this; it says when the candidate was captured, not when they were judged.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/leads" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"requisition_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","channel_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingLeadsCreate({});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads', {
method: 'POST',
headers: {
'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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.FLOWApi(client)
result = api.sourcing_leads_create()import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/leads',
headers={
'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
'Idempotency-Key': str(uuid.uuid4()),
},
json={"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "channel_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingLeadsCreate();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads');
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 => '{"requisition_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","channel_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingLeadsCreate();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads"))
.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("""
{
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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 FLOWApi(config);
var result = api.SourcingLeadsCreate();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/leads");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
{
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_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.FLOWAPI.SourcingLeadsCreate(ctx).Execute()body := strings.NewReader(`{
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/leads", 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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "lead",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"fit_score": 0,
"consent": {
"lawful_basis": "consent",
"channel": "example",
"wording_version": "2026.08.1",
"captured_at": "2026-09-01T09:00:00Z"
},
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"screening_outcome": "in_progress",
"top_factors": [
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
}
],
"duplicate_of": "example",
"promoted_subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"captured_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/leads/{lead_id}#Open one candidate's record
sourcing.leads.retrieve
This reads one lead back, which is one candidate's answer to one of your requisitions. So you can fill a candidate card in your product from the identifier you stored, without keeping your own copy of the score and the consent.
Send the lead's id in the path. You get the lead: its score, where screening has got to, and the channel it came through.
The lead carries the outcome of screening and nothing behind it. Read the questions at GET /v1/sourcing/leads/{lead_id}/screening, and the channel credit at GET /v1/sourcing/leads/{lead_id}/attribution.
Path parameters
lead_id
string
requiredThe lead's identifier: the id of a lead from GET /v1/sourcing/leads, from the response to POST /v1/sourcing/leads, or from a lead.captured webhook. It starts with flow_enterprise_lead_.
Returns
The candidate's record as it stands now: score, screening outcome, channel and consent.
id
string
requiredThe lead's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as lead_id on GET /v1/sourcing/leads/{lead_id} and the /screening, /attribution and /promote routes under it.
object
always "lead"
requiredAlways lead. 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.
requisition_id
string
requiredThe requisition this lead answered: its id from POST /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. With requisition_version, it pins the exact version the lead was captured against.
requisition_version
integer
requiredWhich version produced this lead. A shortlist is reproducible against it.
channel_id
string
optionalThe channel the lead came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. It is what your yield at GET /v1/sourcing/analytics counts against.
screening_outcome
string
optionalin_progresspassedfailedabandonedfit_score
number · minimum 0 · maximum 100
requiredHow well the lead fits the requisition, from 0 to 100. The three factors behind it are in top_factors, so a score never arrives without a reason.
top_factors
array of object
optionalThe three factors that most influenced the score. Three, always, so a score is never a number without a reason.
2 fields
factor
string
requiredThe name of one of the three things that most influenced fit_score. Read it with contribution to see how far it moved the score.
contribution
number
requiredHow much this factor moved fit_score. Read the three together to see why the lead scored as it did.
consent
LeadConsent
requiredCaptured at first contact, before anything else is asked.
4 fields of LeadConsent
lawful_basis
string
requiredThe lawful basis you process this candidate's data under: consent (they agreed), legitimate_interest (your hiring interest) or contract (steps toward a contract with them). Recorded at first contact, before anything else is asked.
consentlegitimate_interestcontractchannel
string
requiredThe channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.
wording_version
string
requiredThe exact wording presented. Answered from the record, not the current page.
captured_at
string · date-time
requiredWhen the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.
duplicate_of
string · nullable
optionalSet when this response matched an existing lead on contact identity.
promoted_subject_ref
string · nullable
optionalSet once you promote the lead at POST /v1/sourcing/leads/{lead_id}/promote, null before: the person's opaque reference, starting with sub_, that you send as subject_ref at POST /v1/identity/consent_tokens and POST /v1/identity/verifications.
captured_at
string · date-time
optionalWhen the lead came in, as an RFC 3339 timestamp in UTC. Screening can still be in_progress after this; it says when the candidate was captured, not when they were judged.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingLeadsRetrieve({ leadId: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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.FLOWApi(client)
result = api.sourcing_leads_retrieve(lead_id='flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingLeadsRetrieve(lead_id: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingLeadsRetrieve("flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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 FLOWApi(config);
var result = api.SourcingLeadsRetrieve(leadId: "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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.FLOWAPI.SourcingLeadsRetrieve(ctx, "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "lead",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"fit_score": 0,
"consent": {
"lawful_basis": "consent",
"channel": "example",
"wording_version": "2026.08.1",
"captured_at": "2026-09-01T09:00:00Z"
},
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"screening_outcome": "in_progress",
"top_factors": [
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
}
],
"duplicate_of": "example",
"promoted_subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"captured_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/leads/{lead_id}/screening#See the working behind a pass or fail
sourcing.screenings.retrieve
This opens the screening one lead's outcome came from. The lead itself carries the outcome and nothing behind it, so this is where your product reads why a candidate was dropped, question by question.
Send the lead's id in the path. You get that lead's one screening: its outcome, the surface it ran on, the exchanges behind it, and decided_by.
The rule a decision names is one of the requisition's own, listed at GET /v1/sourcing/requisitions/{requisition_id}/knockout_rules.
Path parameters
lead_id
string
requiredThe lead's identifier: the id of a lead from GET /v1/sourcing/leads, from the response to POST /v1/sourcing/leads, or from a lead.captured webhook. It starts with flow_enterprise_lead_.
Returns
Every question this candidate was asked, and what your rule decided on each answer.
object
always "screening"
requiredAlways screening. Tells you which kind of record you are looking at, so one handler can read any response.
lead_id
string
requiredThe lead this screening belongs to: the lead_id you put in the path at GET /v1/sourcing/leads/{lead_id}/screening, that lead's id starting with flow_enterprise_lead_. Returned so the record stands on its own.
outcome
string
requiredin_progresspassedfailedabandonedsurface
string
requiredWhere the candidate answered: whatsapp (in a WhatsApp conversation) or web (on a web page). The rules that decide pass and fail are the same on either.
whatsappwebexchanges
array of object
requiredOne entry per question put to the candidate: the question, how it was answered, what was extracted, and what the rule decided. Never more than the question_cap on the requisition's knockout rules, five by default.
4 fields
question
string
requiredThe question as it was put to the candidate. The wording can differ from the knockout rule's question; the rule it was asked for is named in decision.rule.
answer_mode
string
requiredHow the candidate answered: text (typed) or voice (spoken). Either way, extracted holds what was taken from the answer and the same rule judges it.
textvoiceextracted
object
requiredWhat was extracted from the candidate's answer. It decides nothing.
3 fields
field
string
optionalThe fact the answer was read for, named as your rule names it: years_experience for a rule of years_experience >= 3.
value
string
optionalWhat the answer was taken to say for that field, as a string. It is what the rule is applied to; a wrong value here is an extraction problem, not a rule problem.
confidence
number
optionalHow confident the extraction is that value is what the candidate meant. It changes no decision; a low figure beside a failed rule tells you to check the extraction first.
decision
object
requiredThe decision on the answer, and the rule that made it. This is the only thing that decides.
3 fields
rule
string
requiredThe knockout rule that was applied, written out exactly as you gave it, such as years_experience >= 3. What it decided is in passed.
passed
boolean
requiredtrue when the extracted value met the rule, false when it didn't. Decided by the rule alone, never by the conversation.
rule_pack_version
string
optionalThe version of the rule pack that judged this answer, such as 2026.08.1. Rules change between versions, and this names the one that applied.
decided_by
always "rule_pack"
optionalAlways rule_pack. The decision comes from your knockout rules, never from the conversation.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingScreeningsRetrieve({ leadId: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening', {
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.FLOWApi(client)
result = api.sourcing_screenings_retrieve(lead_id='flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingScreeningsRetrieve(lead_id: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingScreeningsRetrieve("flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening"))
.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 FLOWApi(config);
var result = api.SourcingScreeningsRetrieve(leadId: "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening");
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.FLOWAPI.SourcingScreeningsRetrieve(ctx, "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/screening", 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": "screening",
"lead_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"outcome": "in_progress",
"surface": "whatsapp",
"exchanges": [
{
"question": "example",
"answer_mode": "text",
"extracted": {
"field": "gross.amount",
"value": "example",
"confidence": 1
},
"decision": {
"rule": "years_experience >= 3",
"passed": true,
"rule_pack_version": "2026.08.1"
}
}
],
"decided_by": "rule_pack"
}
/v1/sourcing/leads/{lead_id}/promote#Promote a lead toward identity
sourcing.leads.promote
This moves one candidate out of sourcing and on toward identity. So a candidate who answered your advert does not become a person in your records until you say so, one lead at a time.
Send the lead's id in the path and no request body. You get the lead back, with its fit_score and the requisition_version that produced it.
A lead has to exist first, either one the channels brought you or one you recorded at POST /v1/sourcing/leads.
Promotion goes one way. No call takes a lead back out, so promote when you have decided about the candidate and not before.
Path parameters
lead_id
string
requiredThe lead's identifier: the id of a lead from GET /v1/sourcing/leads, from the response to POST /v1/sourcing/leads, or from a lead.captured webhook. It starts with flow_enterprise_lead_.
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 same lead record, now promoted, to store against the candidate in your product.
id
string
requiredThe lead's identifier. It starts with flow_enterprise_lead_ and never changes; pass it as lead_id on GET /v1/sourcing/leads/{lead_id} and the /screening, /attribution and /promote routes under it.
object
always "lead"
requiredAlways lead. 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.
requisition_id
string
requiredThe requisition this lead answered: its id from POST /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. With requisition_version, it pins the exact version the lead was captured against.
requisition_version
integer
requiredWhich version produced this lead. A shortlist is reproducible against it.
channel_id
string
optionalThe channel the lead came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. It is what your yield at GET /v1/sourcing/analytics counts against.
screening_outcome
string
optionalin_progresspassedfailedabandonedfit_score
number · minimum 0 · maximum 100
requiredHow well the lead fits the requisition, from 0 to 100. The three factors behind it are in top_factors, so a score never arrives without a reason.
top_factors
array of object
optionalThe three factors that most influenced the score. Three, always, so a score is never a number without a reason.
2 fields
factor
string
requiredThe name of one of the three things that most influenced fit_score. Read it with contribution to see how far it moved the score.
contribution
number
requiredHow much this factor moved fit_score. Read the three together to see why the lead scored as it did.
consent
LeadConsent
requiredCaptured at first contact, before anything else is asked.
4 fields of LeadConsent
lawful_basis
string
requiredThe lawful basis you process this candidate's data under: consent (they agreed), legitimate_interest (your hiring interest) or contract (steps toward a contract with them). Recorded at first contact, before anything else is asked.
consentlegitimate_interestcontractchannel
string
requiredThe channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.
wording_version
string
requiredThe exact wording presented. Answered from the record, not the current page.
captured_at
string · date-time
requiredWhen the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.
duplicate_of
string · nullable
optionalSet when this response matched an existing lead on contact identity.
promoted_subject_ref
string · nullable
optionalSet once you promote the lead at POST /v1/sourcing/leads/{lead_id}/promote, null before: the person's opaque reference, starting with sub_, that you send as subject_ref at POST /v1/identity/consent_tokens and POST /v1/identity/verifications.
captured_at
string · date-time
optionalWhen the lead came in, as an RFC 3339 timestamp in UTC. Screening can still be in_progress after this; it says when the candidate was captured, not when they were judged.
Other responses
Errors it can return
curl -X POST "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingLeadsPromote({ leadId: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote', {
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.FLOWApi(client)
result = api.sourcing_leads_promote(lead_id='flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingLeadsPromote(lead_id: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingLeadsPromote("flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote"))
.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 FLOWApi(config);
var result = api.SourcingLeadsPromote(leadId: "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote");
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.FLOWAPI.SourcingLeadsPromote(ctx, "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/promote", 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": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "lead",
"livemode": false,
"mocked": true,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"requisition_version": 1,
"fit_score": 0,
"consent": {
"lawful_basis": "consent",
"channel": "example",
"wording_version": "2026.08.1",
"captured_at": "2026-09-01T09:00:00Z"
},
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"screening_outcome": "in_progress",
"top_factors": [
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
},
{
"factor": "example",
"contribution": 1
}
],
"duplicate_of": "example",
"promoted_subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"captured_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/leads/{lead_id}/attribution#See where a candidate came from
sourcing.attributions.retrieve
This says which channel a lead first came through, and which one it came through last. So you can credit a hire to the channel that found the candidate, and tell that apart from the one they came back through.
Send the lead's id in the path. You get first_touch and last_touch, each naming a channel and when the contact happened.
A touch carries capture_link_id only when the candidate followed a link, so check it is there before you read it.
Carry a touch's channel_id to GET /v1/sourcing/channels/{channel_id} to see what that channel costs you.
Path parameters
lead_id
string
requiredThe lead's identifier: the id of a lead from GET /v1/sourcing/leads, from the response to POST /v1/sourcing/leads, or from a lead.captured webhook. It starts with flow_enterprise_lead_.
Returns
Where this candidate first and last came from, for crediting a channel with the hire.
object
always "attribution"
requiredAlways attribution. Tells you which kind of record you are looking at, so one handler can read any response.
lead_id
string
requiredThe lead this attribution belongs to: the lead_id from the path at GET /v1/sourcing/leads/{lead_id}/attribution, starting with flow_enterprise_lead_. It survives promotion, so you can still ask which channel produced the person.
first_touch
object
requiredThe first contact this lead made: which channel it came through, the capture link if one was used, and when. Written once and never changed.
3 fields
channel_id
string
requiredThe channel the first contact came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Written once and never changed.
capture_link_id
string
optionalThe capture link the first contact came through: the id of a link from POST /v1/sourcing/requisitions/{requisition_id}/capture_links, starting with flow_enterprise_lead_. Absent when the candidate arrived without a link.
at
string · date-time
requiredWhen the first contact happened, as an RFC 3339 timestamp in UTC.
last_touch
object
requiredThe latest contact this lead made: which channel it came through, the capture link if one was used, and when. Equal to first_touch when there was only one contact.
3 fields
channel_id
string
requiredThe channel the latest contact came through: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. Equal to first_touch.channel_id when there was only one contact.
capture_link_id
string
optionalThe capture link the latest contact came through: the id of a link from POST /v1/sourcing/requisitions/{requisition_id}/capture_links, starting with flow_enterprise_lead_. Absent when the candidate arrived without a link.
at
string · date-time
requiredWhen the latest contact happened, as an RFC 3339 timestamp in UTC.
referring_party
string · nullable
optionalWho referred the candidate, when the lead came by referral, and null when nobody did. Like the touches, it can't change once written.
survives_promotion
always true
optionalAlways true. Promoting the lead toward identity leaves this record in place, so you can ask which channel produced a hire long after the hire.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingAttributionsRetrieve({ leadId: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution', {
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.FLOWApi(client)
result = api.sourcing_attributions_retrieve(lead_id='flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingAttributionsRetrieve(lead_id: 'flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingAttributionsRetrieve("flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution"))
.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 FLOWApi(config);
var result = api.SourcingAttributionsRetrieve(leadId: "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution");
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.FLOWAPI.SourcingAttributionsRetrieve(ctx, "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/leads/flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/attribution", 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": "attribution",
"lead_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"first_touch": {
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"at": "2026-09-01T09:00:00Z",
"capture_link_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
"last_touch": {
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"at": "2026-09-01T09:00:00Z",
"capture_link_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
},
"referring_party": "example",
"survives_promotion": true
}
/v1/sourcing/analytics#See which channels produce hires
sourcing.analytics.retrieve
The channel that sends the most leads is not always the one that fills the job. This is what your dashboard argues next quarter's sourcing budget from, and it runs as far as the hire.
Send nothing to get every channel your organisation has used, or narrow the figures with requisition_id, from and to. You get one entry per channel for the period you asked for.
The one money figure here is cost_per_qualified_lead, and it is not always present. What you have spent against a ceiling, and what is left of it, are at GET /v1/sourcing/spend_ceilings.
A channel arrives as a channel_id and no name. Pass it to GET /v1/sourcing/channels/{channel_id} for the label you put in front of a person.
Query parameters
requisition_id
string
optionalRestricts the figures to one requisition: its id from POST /v1/sourcing/requisitions or GET /v1/sourcing/requisitions, starting with flow_enterprise_requisition_. Leave it out to get your yield across every requisition.
from
string
optionalThe earliest date to count from, as YYYY-MM-DD. Leave it out and nothing is cut off at the start; pair it with to to bound a period.
to
string
optionalThe latest date to count to, as YYYY-MM-DD. Leave it out and the figures run to today; pair it with from to bound a period.
Returns
Each channel's counts and cost over the period, for comparing one channel with another.
object
always "channel_analytics"
requiredAlways channel_analytics. Tells you which kind of record you are looking at, so one handler can read any response.
scope
always "requesting_organisation"
requiredYour own figures only. Never another organisation's yield on a channel.
channels
array of object
requiredOne entry per channel: the leads it captured, how many qualified, how many you promoted and how many were hired, and what each qualified lead cost you there. Your own figures only.
6 fields
channel_id
string
requiredThe channel these figures are for, by its id as GET /v1/sourcing/channels lists it, prefix flow_enterprise_broadcast_. Pass it to GET /v1/sourcing/channels/{channel_id} to read the channel.
captured
integer
requiredHow many leads this channel captured in the period. One person answering twice on the same requisition is one lead, so they count once.
qualified
integer
requiredHow many of this channel's leads passed your knockout rules in the period. Divide it by captured for the channel's qualification rate.
promoted
integer
requiredHow many of those leads you promoted toward identity in the period, with POST /v1/sourcing/leads/{lead_id}/promote.
hired
integer
requiredHow many of those leads went on to be hired in the period. Attribution survives promotion, so the hire is still credited to the channel that produced the lead.
cost_per_qualified_lead
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.
Other responses
Errors it can return
# query parameters: requisition_id (optional), from (optional), to (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
// query parameters: requisition_id (optional), from (optional), to (optional)
const result = await api.sourcingAnalyticsRetrieve({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', from: '2026-01-01', to: '2027-01-01' });// query parameters: requisition_id (optional), from (optional), to (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01', {
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.FLOWApi(client)
# query parameters: requisition_id (optional), from (optional), to (optional)
result = api.sourcing_analytics_retrieve(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', from='2026-01-01', to='2027-01-01')import os
import requests
# query parameters: requisition_id (optional), from (optional), to (optional)
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01',
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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: requisition_id (optional), from (optional), to (optional)
$result = $api->sourcingAnalyticsRetrieve(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', from: '2026-01-01', to: '2027-01-01');<?php
// query parameters: requisition_id (optional), from (optional), to (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: requisition_id (optional), from (optional), to (optional)
var result = api.sourcingAnalyticsRetrieve("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "2026-01-01", "2027-01-01");// query parameters: requisition_id (optional), from (optional), to (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01"))
.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 FLOWApi(config);
// query parameters: requisition_id (optional), from (optional), to (optional)
var result = api.SourcingAnalyticsRetrieve(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", from: "2026-01-01", to: "2027-01-01");// query parameters: requisition_id (optional), from (optional), to (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01");
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: requisition_id (optional), from (optional), to (optional)
result, _, err := client.FLOWAPI.SourcingAnalyticsRetrieve(ctx).RequisitionId("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").From("2026-01-01").To("2027-01-01").Execute()// query parameters: requisition_id (optional), from (optional), to (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=2026-01-01&to=2027-01-01", 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": "channel_analytics",
"scope": "requesting_organisation",
"channels": [
{
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"captured": 1,
"qualified": 1,
"promoted": 1,
"hired": 1,
"cost_per_qualified_lead": {
"amount": 1500000,
"currency": "NGN"
}
}
]
}
/v1/sourcing/readiness#Check FLOW is ready to source
sourcing.readiness.retrieve
What FLOW needs before it will source is published here, so your code reads one field where a person would otherwise read a checklist. Nothing is created or changed here, so an onboarding screen or a setup script can read it before anything else runs.
Send no parameters. You get ready as the single answer, the requirements behind it, and the gates no configuration of yours can lift.
Returns
What FLOW requires of you, and whether it has what it 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.
channels_without_credentials
array of string
optionalThe channels you hold no credentials for. Nothing broadcasts to one of them until you supply the credentials; credentials_held on the channel says the same thing.
wallet_funded
boolean
optionalPaid channels cannot run without it, and the ceiling check refuses first.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/readiness" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingReadinessRetrieve({});const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/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.FLOWApi(client)
result = api.sourcing_readiness_retrieve()import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingReadinessRetrieve();<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingReadinessRetrieve();var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/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 FLOWApi(config);
var result = api.SourcingReadinessRetrieve();using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/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.FLOWAPI.SourcingReadinessRetrieve(ctx).Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/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": "flow",
"mode": "integrated",
"ready": true,
"rows": [],
"always_enforced": [
"consent_token",
"bank_account_validation",
"tax_identifier_validation",
"duplicate_detection"
],
"channels_without_credentials": [],
"wallet_funded": true
}
/v1/sourcing/deliveries/{delivery_id}#What one channel did with a send
sourcing.deliveries.retrieve
A delivery is one channel's record of one broadcast. This reads one back on its own, so your product can open the single channel somebody is asking about without pulling a whole send.
Send the delivery's id in the path and you get that one record, with the broadcast and the requisition it belongs to.
POST /v1/sourcing/broadcasts/{broadcast_id}/deliveries/{delivery_id}/retry takes the broadcast's id as well as the delivery's, and this record carries both. An id that matches no delivery of yours answers not_found.
Path parameters
delivery_id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_: the id of an entry in deliveries on a broadcast, of a delivery listed at GET /v1/sourcing/deliveries, or of one you recorded at POST /v1/sourcing/deliveries.
Returns
One channel's record of one send, for the detail behind a single outcome.
id
string
requiredThe delivery's identifier, starting with flow_enterprise_delivery_; it never changes. It is in a broadcast's deliveries, at GET /v1/sourcing/deliveries and from POST /v1/sourcing/deliveries; pass it as delivery_id to read or retry it.
object
always "channel_delivery"
requiredAlways channel_delivery. Tells you which kind of record you are looking at, so one handler can read any response.
broadcast_id
string
requiredThe broadcast this delivery is part of: the id, starting with flow_enterprise_broadcast_, from POST /v1/sourcing/requisitions/{requisition_id}/broadcasts, or the one you named as broadcast_id at POST /v1/sourcing/deliveries.
requisition_id
string
optionalThe requisition this delivery carried: the id of the requisition behind the broadcast, starting with flow_enterprise_requisition_, from POST /v1/sourcing/requisitions. Filter GET /v1/sourcing/deliveries by it to see every channel it went to.
channel_id
string
requiredThe channel this delivery went to: the id of a channel from GET /v1/sourcing/channels, starting with flow_enterprise_broadcast_. One delivery covers one channel, so a broadcast has one delivery per channel it went to.
state
string
requiredfailed is terminal and always reported. A channel you selected is never dropped silently.
queuedsendingdeliveredretryingfailedattempts
integer · minimum 0
requiredHow many times posting to this channel has been tried so far, 0 before the first try. Read it with state and last_fault to see how a channel is doing.
next_attempt_at
string · date-time · nullable
optionalExponential backoff. Absent once the state is terminal.
last_fault
string · nullable
optionalWhy the most recent attempt at this channel failed, in plain words; null while nothing has failed. Read it before you retry.
external_reference
string · nullable
optionalThe channel's own reference for this posting, where the channel gives one. null otherwise, so name the delivery by id, never by this.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingDeliveriesRetrieve({ deliveryId: '{delivery_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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.FLOWApi(client)
result = api.sourcing_deliveries_retrieve(delivery_id='{delivery_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingDeliveriesRetrieve(delivery_id: '{delivery_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingDeliveriesRetrieve("{delivery_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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 FLOWApi(config);
var result = api.SourcingDeliveriesRetrieve(deliveryId: "{delivery_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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.FLOWAPI.SourcingDeliveriesRetrieve(ctx, "{delivery_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/deliveries/%7Bdelivery_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": "flow_enterprise_delivery_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "channel_delivery",
"broadcast_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"state": "queued",
"attempts": 0,
"requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"next_attempt_at": "2026-09-01T09:00:00Z",
"last_fault": "The register did not answer within the timeout.",
"external_reference": "paye-2026-09-rivers"
}
/v1/sourcing/requisitions/{requisition_id}/quarantine#Hold a role back before it goes out
sourcing.requisitions.quarantine
This holds a role back: it stays readable, and it stops being one you can publish. So a complaint about the wording, or a check of your own, can stop a vacancy on the record itself and not in somebody's inbox.
Send the requisition's id in the path and no request body. The requisition comes back in quarantined, and a requisition.quarantined event goes to your webhook endpoints.
Hold a role before POST /v1/sourcing/requisitions/{requisition_id}/publish has run on it. One already published answers conflict.
No call here takes a role back out of it, and publishing is refused with conflict while it is held. Where the wording is the problem, a fresh POST /v1/sourcing/requisitions is the way on.
Path parameters
requisition_id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/requisitions or listed at GET /v1/sourcing/requisitions, or one of the requisition_ids on a bulk upload.
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 requisition, held where you can still read it but not publish it.
id
string
requiredThe requisition's identifier, starting with flow_enterprise_requisition_; it never changes. You get it from POST /v1/sourcing/requisitions (or in requisition_ids on a bulk upload) and pass it as requisition_id on every call about it.
object
always "requisition"
requiredAlways requisition. 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
requiredA requisition sits in pending_confirmation while an inferred pay figure, headcount, location or start date is unconfirmed. It cannot leave that state until you confirm.
pending_confirmationdraftunder_reviewquarantinedpublishedclosedversion
integer · minimum 1
requiredAn edit after first broadcast makes a new version. Leads stay on theirs.
job_title
string
requiredThe title of the role as you gave it. The occupation code is inferred from it when you don't send one, so a local or colloquial title is fine.
occupation_code
string
optionalThe occupation this role maps to in the taxonomy, such as ng-7412. Inferred from the title when you didn't send one, and listed in inferred_fields until you confirm it.
employment_type
string
optionalpermanentfixed_termcontractcasualapprenticeshipheadcount
integer · minimum 1
requiredHow many people you are hiring for this role, at least 1. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
compensation
Compensation
optional3 fields of Compensation
amount
Money
required2 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.
period
string
requiredThe span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.
monthlyannualdailyper_tasknegotiable
boolean
optionalWhether the figure is open to negotiation: true when it is, false when it is fixed.
location
Location
optional3 fields of Location
description
string
requiredWhere the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.
state
string
optionalThe Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.
geofence_radius_metres
integer · nullable
optionalA radius around the location, in whole metres, or null when there is none. If it had to be inferred it comes back in inferred_fields for you to confirm.
starts_on
string · date · nullable
optionalThe date the role starts, as YYYY-MM-DD, or null when none is set. It never broadcasts on inference alone; confirm it if it appears in inferred_fields.
inferred_fields
array of string
optionalFields that were inferred rather than read from what you sent. Each has to be confirmed, and compensation, headcount, location and start date can never broadcast while they are here.
quarantine
one of
optionalWhy the requisition was held: the rule it tripped, the rule pack version, the detail and the offending text. null when nothing held it; fix what it names before you publish.
QuarantineFindingorcreated_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/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingRequisitionsQuarantine({ requisitionId: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine', {
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.FLOWApi(client)
result = api.sourcing_requisitions_quarantine(requisition_id='flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')import os
import uuid
import requests
response = requests.request(
'POST',
'https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine',
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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingRequisitionsQuarantine(requisition_id: 'flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine');
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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingRequisitionsQuarantine("flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine"))
.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 FLOWApi(config);
var result = api.SourcingRequisitionsQuarantine(requisitionId: "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine");
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.FLOWAPI.SourcingRequisitionsQuarantine(ctx, "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/sourcing/requisitions/flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/quarantine", 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": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
"object": "requisition",
"livemode": false,
"mocked": true,
"status": "quarantined",
"version": 1,
"job_title": "example",
"headcount": 1,
"occupation_code": "ng-7412",
"employment_type": "permanent",
"compensation": {
"amount": {
"amount": 7500000,
"currency": "NGN"
},
"period": "monthly",
"negotiable": true
},
"location": {
"description": "example",
"state": "Lagos",
"geofence_radius_metres": 1
},
"starts_on": "2026-09-01",
"inferred_fields": [
"example"
],
"quarantine": {
"rule_code": "discriminatory_criteria",
"rule_pack_version": "2026.08.1",
"detail": "The payee has no verified destination, so this line cannot be paid.",
"contested": true,
"offending_text": "example"
},
"created_at": "2026-09-01T09:00:00Z"
}
/v1/sourcing/events#Replay one record's event history
sourcing.events.list
FLOW keeps each record's history as events, and this is where you read it back. So a webhook receiver that was down can catch up from the last event it handled, and your product can show how a requisition reached its current state.
Send nothing and your events come back oldest first, across every record, each under an evt_ identifier. Name one record's id as stream, with the sequence you last handled as after, and you get that record's own history in sequence order.
Across records, events come back in the order FLOW recorded them, which is not always occurred_at order. Sort on occurred_at yourself where a reader needs the timeline.
Read any one of them again at GET /v1/sourcing/events/{event_id}.
Query parameters
stream
string
optionalThe id of the record whose events you want, such as a requisition's id from POST /v1/sourcing/requisitions, prefix flow_enterprise_requisition_. Leave it out to get events across every record, paged with starting_after.
after
integer
optionalThe sequence of the last event you handled on this stream, as it reads on each event at GET /v1/sourcing/events; you get what came after it, and 0 reads from the start. Needs stream beside it, because a sequence counts within one stream.
limit
integer
optionalHow many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.
starting_after
string
optionalThe id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.
Returns
A page of events, oldest first, to replay one record's history in order.
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 requisition's id from POST /v1/sourcing/requisitions (flow_enterprise_requisition_). sequence counts within this stream only; replay with stream and after at GET /v1/sourcing/events.
data
object
requiredThe record the event is about, in the shape its type names: a requisition, a channel delivery, a lead, or your spend ceilings. 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/sourcing/events?stream=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(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.sourcingEventsList({ stream: 'flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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.FLOWApi(client)
# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
result = api.sourcing_events_list(stream='flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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\FLOWApi(new GuzzleHttp\Client(), $config);
# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
$result = $api->sourcingEventsList(stream: 'flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.sourcingEventsList("flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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 FLOWApi(config);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.SourcingEventsList(stream: "flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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.FLOWAPI.SourcingEventsList(ctx).Stream("flow_enterprise_requisition_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/sourcing/events?stream=flow_enterprise_requisition_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/sourcing/events/{event_id}#Open one event by its identifier
sourcing.events.retrieve
This turns one event identifier into the whole event: what happened, which of your records it happened to, and where it sits in that record's history. So an identifier you kept from a webhook delivery, or from your own log, is enough to show the event again.
Send the event's id in the path, the one starting evt_, and nothing else. You get that one event in full.
Pass this event's stream and sequence to GET /v1/sourcing/events as stream and after to read what came next. An identifier that is not yours reads the same as one that never existed, so this cannot tell you what another organisation holds.
Path parameters
event_id
string
requiredThe event's id, as it reads on an event you listed at GET /v1/sourcing/events or received on a webhook delivery. It starts with evt_.
Returns
The one event, with the record it is about and its place in that record's history.
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 requisition's id from POST /v1/sourcing/requisitions (flow_enterprise_requisition_). sequence counts within this stream only; replay with stream and after at GET /v1/sourcing/events.
data
object
requiredThe record the event is about, in the shape its type names: a requisition, a channel delivery, a lead, or your spend ceilings. The same body a webhook delivery carries.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/events/%7Bevent_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingEventsRetrieve({ eventId: '{event_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/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.FLOWApi(client)
result = api.sourcing_events_retrieve(event_id='{event_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingEventsRetrieve(event_id: '{event_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingEventsRetrieve("{event_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/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 FLOWApi(config);
var result = api.SourcingEventsRetrieve(eventId: "{event_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/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.FLOWAPI.SourcingEventsRetrieve(ctx, "{event_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/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/sourcing/audit_entries#Who changed what, and who was refused
sourcing.audit_entries.list
This answers who changed something in your organisation and what came of it. So your product can show who published a requisition or promoted a lead, and which attempts were refused, without keeping a log of its own.
Send nothing, or narrow by actor, by record, by outcome, or to one window of time. Entries come back newest first.
A value for action that is not exactly what an entry stores gives you an empty page, not a refusal. So read an empty page as a question about the spelling first.
This log covers the sourcing routes only. An attempt on another part of the platform is in that part's own list.
Open any one of them again at GET /v1/sourcing/audit_entries/{audit_entry_id}.
Query parameters
action
string
optionalThe method and route pattern that was attempted, exactly as action reads on each entry, such as POST /v1/sourcing/requisitions. Leave it out to get attempts on every route.
actor_id
string
optionalThe id of the API key (prefix key_) or staff account that made the attempt, exactly as actor_id reads on each entry. Leave it out to get every actor; pair it with outcome=refused to see repeated refusals on one credential.
resource
string
optionalThe collection segment of the route that was attempted, exactly as resource reads on each entry, such as requisitions, broadcasts or leads. Leave it out to get every kind of record; pair it with resource_id to narrow to one.
resource_id
string
optionalThe id of the record the route named, exactly as resource_id reads on each entry, such as a requisition's id from POST /v1/sourcing/requisitions (flow_enterprise_requisition_). Leave it out to get every record; pair it with resource.
outcome
string
optionalOne outcome only: succeeded (a 2xx answer), refused (a 4xx) or failed (a 5xx). Leave it out to get every outcome; ask for refused to see every attempt that was turned away.
succeededrefusedfailedrecorded_after
string
optionalOnly entries made after this moment, as an RFC 3339 timestamp in UTC, compared with at on each entry; an entry at exactly this instant is left out. Leave it out to reach back to your oldest entry.
recorded_before
string
optionalOnly entries made before this moment, as an RFC 3339 timestamp in UTC, compared with at on each entry; an entry at exactly this instant is left out. Leave it out to read up to now; pair it with recorded_after to read one window.
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 attempts, newest first, with the actor, the route and the status for each.
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/sourcing/audit_entries/{audit_entry_id} to read this entry again, 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 is the route and not the answer: a refused attempt and a replayed idempotent request aimed at a mocked route both say 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.
request_id
string
requiredThe Droomwork-Request-Id the call was answered with. It is on every response, refusals included, so a refusal you were shown can be matched to its entry here.
actor_type
string
requiredWhat kind of credential made the attempt. client is one of your API keys or OAuth clients; staff is a Droomwork staff member on a support grant, the same access /v1/support_access shows you.
actor_id
string
requiredWho made the attempt: with actor_type client, the API key's id as GET /v1/api_keys lists it or the client_id you send to POST /v1/oauth/token, prefix key_; with staff, the staff account's id. Filter by it to follow one credential.
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 name in the route, such as requisitions or leads. null when the call reached no route at all.
resource_id
string · nullable
optionalThe id of the record the route named, such as a requisition's id (flow_enterprise_requisition_) on POST /v1/sourcing/requisitions/{requisition_id}/publish. null for an attempt on a collection, such as creating or listing.
outcome
string
requiredHow the attempt ended: succeeded for a 2xx status, refused for a 4xx, failed for a 5xx. It is read from status, so the two never disagree.
succeededrefusedfailedstatus
integer
requiredThe HTTP status the caller was given, such as 201 or 403. It is what outcome is read from.
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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(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.sourcingAuditEntriesList({ action: 'POST /v1/sourcing/requisitions', actorId: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'requisitions', resourceId: 'flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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.FLOWApi(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.sourcing_audit_entries_list(action='POST /v1/sourcing/requisitions', actor_id='sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource='requisitions', resource_id='flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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\FLOWApi(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->sourcingAuditEntriesList(action: 'POST /v1/sourcing/requisitions', actor_id: 'sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'requisitions', resource_id: 'flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(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.sourcingAuditEntriesList("POST /v1/sourcing/requisitions", "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "requisitions", "flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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 FLOWApi(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.SourcingAuditEntriesList(action: "POST /v1/sourcing/requisitions", actorId: "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", resource: "requisitions", resourceId: "flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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.FLOWAPI.SourcingAuditEntriesList(ctx).Action("POST /v1/sourcing/requisitions").ActorId("sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Resource("requisitions").ResourceId("flow_enterprise_requisition_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/sourcing/audit_entries?action=POST%20%2Fv1%2Fsourcing%2Frequisitions&actor_id=sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=requisitions&resource_id=flow_enterprise_requisition_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": "requisitions",
"resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}
],
"has_more": true
}
/v1/sourcing/audit_entries/{audit_entry_id}#Open one recorded attempt
sourcing.audit_entries.retrieve
This turns one entry identifier into the attempt it names: who made it, what they aimed at, and the answer they were given. So wherever your product keeps that identifier, a reader can open the same recorded attempt again instead of searching the log for it.
Send the entry's id in the path and nothing else. You get that one entry, carrying the request_id of the attempt it records, which is what lines it up against your own logs.
An identifier that is not yours reads the same as one that never existed, so this cannot tell you what another organisation holds.
Path parameters
audit_entry_id
string
requiredThe entry's id, as it reads on an entry you listed at GET /v1/sourcing/audit_entries. It starts with audit_entry_.
Returns
The one attempt: who made it, what it was aimed at, and the answer given.
id
string
requiredThe entry's identifier. It starts with audit_entry_ and never changes; pass it to GET /v1/sourcing/audit_entries/{audit_entry_id} to read this entry again, 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 is the route and not the answer: a refused attempt and a replayed idempotent request aimed at a mocked route both say 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.
request_id
string
requiredThe Droomwork-Request-Id the call was answered with. It is on every response, refusals included, so a refusal you were shown can be matched to its entry here.
actor_type
string
requiredWhat kind of credential made the attempt. client is one of your API keys or OAuth clients; staff is a Droomwork staff member on a support grant, the same access /v1/support_access shows you.
actor_id
string
requiredWho made the attempt: with actor_type client, the API key's id as GET /v1/api_keys lists it or the client_id you send to POST /v1/oauth/token, prefix key_; with staff, the staff account's id. Filter by it to follow one credential.
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 name in the route, such as requisitions or leads. null when the call reached no route at all.
resource_id
string · nullable
optionalThe id of the record the route named, such as a requisition's id (flow_enterprise_requisition_) on POST /v1/sourcing/requisitions/{requisition_id}/publish. null for an attempt on a collection, such as creating or listing.
outcome
string
requiredHow the attempt ended: succeeded for a 2xx status, refused for a 4xx, failed for a 5xx. It is read from status, so the two never disagree.
succeededrefusedfailedstatus
integer
requiredThe HTTP status the caller was given, such as 201 or 403. It is what outcome is read from.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/audit_entries/%7Baudit_entry_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, FLOWApi } from '@droomwork/sdk';
const api = new FLOWApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));
const result = await api.sourcingAuditEntriesRetrieve({ auditEntryId: '{audit_entry_id}' });const response = await fetch('https://sandbox.droomwork.io/v1/sourcing/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.FLOWApi(client)
result = api.sourcing_audit_entries_retrieve(audit_entry_id='{audit_entry_id}')import os
import requests
response = requests.request(
'GET',
'https://sandbox.droomwork.io/v1/sourcing/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\FLOWApi(new GuzzleHttp\Client(), $config);
$result = $api->sourcingAuditEntriesRetrieve(audit_entry_id: '{audit_entry_id}');<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/sourcing/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.FlowApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
FlowApi api = new FlowApi(client);
var result = api.sourcingAuditEntriesRetrieve("{audit_entry_id}");var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/sourcing/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 FLOWApi(config);
var result = api.SourcingAuditEntriesRetrieve(auditEntryId: "{audit_entry_id}");using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/sourcing/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.FLOWAPI.SourcingAuditEntriesRetrieve(ctx, "{audit_entry_id}").Execute()req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/sourcing/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": "requisitions",
"resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}