DDroomwork Developers

Version 1.0.0

Droomwork FLOW ENTERPRISE

Sourcing and screened leads across Nigerian hiring channels.

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

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 rather than 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?

GET/v1/sourcing/requisitions#

List requisitions

sourcing.requisitions.list

Returns your requisitions, newest first.

Query parameters

limit integer optional

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

starting_after string optional

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

status string optional

Only 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_reviewquarantinedpublishedclosed

Returns

A page of requisitions.

object always "list" required

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

data array of Requisition required

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

16 fields of Requisition
id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), status (optional)
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)
Response
{
  "object": "list",
  "data": [
    {
      "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "requisition",
      "livemode": true,
      "mocked": true,
      "status": "pending_confirmation",
      "version": 1,
      "job_title": "example",
      "headcount": 1,
      "occupation_code": "ng-7412",
      "employment_type": "permanent",
      "compensation": {
        "amount": {
          "amount": 1234567,
          "currency": "NGN"
        },
        "period": "monthly",
        "negotiable": true
      },
      "location": {
        "description": "example",
        "state": "example",
        "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
}
POST/v1/sourcing/requisitions#

Create a requisition

sourcing.requisitions.create

You get back the same requisition object whether you create it here, in the dashboard or by bulk upload, and it behaves the same however it arrived.

The job title, occupation code, employment type, headcount, compensation, location, geo-fence radius, start date and knockout criteria are each resolved from what you sent. Anything that had to be inferred is listed on the response for you to confirm.

A requisition carrying discriminatory criteria, unlawful terms or an advance fee pattern is rejected or quarantined. A contested case goes to a person rather than through.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

job_title string required

The 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 optional

Omit and it is inferred from the title, then returned for confirmation.

employment_type string optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How many people you are hiring for this role, at least 1. One of the four fields that never broadcast on inference alone.

compensation Compensation optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

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

What the candidate is asked. It may be phrased differently in the conversation.

rule string required

The deterministic test applied to the extracted answer.

description string optional

The 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 requisition, awaiting confirmation of anything inferred.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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":1234567,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"example","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":1234567,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"example","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": 1234567,
        "currency": "NGN"
      },
      "period": "monthly",
      "negotiable": true
    },
    "location": {
      "description": "example",
      "state": "example",
      "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": 1234567, "currency": "NGN"}, "period": "monthly", "negotiable": True}, "location": {"description": "example", "state": "example", "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": 1234567, "currency": "NGN"}, "period": "monthly", "negotiable": True}, "location": {"description": "example", "state": "example", "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":1234567,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"example","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":1234567,"currency":"NGN"},"period":"monthly","negotiable":true},"location":{"description":"example","state":"example","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": 1234567,
              "currency": "NGN"
            },
            "period": "monthly",
            "negotiable": true
          },
          "location": {
            "description": "example",
            "state": "example",
            "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": 1234567,
          "currency": "NGN"
        },
        "period": "monthly",
        "negotiable": true
      },
      "location": {
        "description": "example",
        "state": "example",
        "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": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
GET/v1/sourcing/requisitions/{requisition_id}#

Retrieve a requisition

sourcing.requisitions.retrieve

Returns the requisition at its current version. Pass version to read an earlier one; that's how you read a lead bound to version 2 after version 3 exists.

Path parameters

requisition_id string required

The 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 optional

Which 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 requisition.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# 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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
POST/v1/sourcing/requisitions/{requisition_id}/confirm_inferences#

Confirm the fields that were inferred

sourcing.requisitions.confirm_inferences

You must do this before you publish. Compensation, headcount, location and start date never broadcast on inference alone, and publish is refused while any inferred field is unconfirmed.

Confirming doesn't mean accepting. If an inference was wrong, send the corrected value here.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

confirmations array of object required

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

The name of the inferred field you are answering for, exactly as it appears in the requisition's inferred_fields.

accepted boolean required

true 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 optional

Supply this when the inference was wrong.

Returns

The requisition, ready to publish.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
POST/v1/sourcing/requisitions/{requisition_id}/publish#

Publish a requisition

sourcing.requisitions.publish

Refused while any inferred field is unconfirmed, and refused while the requisition is quarantined.

Publish again after a first broadcast and you get a new version. Leads you've already captured stay bound to the version that produced them.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The published requisition.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
POST/v1/sourcing/requisitions/{requisition_id}/close#

Close a requisition

sourcing.requisitions.close

Stops further capture. Leads you've already taken stay readable.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The closed requisition.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
POST/v1/sourcing/imports#

Upload requisitions in bulk

sourcing.imports.create

Upload a file of requisitions and get the same requisition objects you'd get creating them one at a time. You get a report naming every bad row, not a stop at the first.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The import, with its validation report.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

Where 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_acceptanceacceptedrejected
requisition_ids array of string optional

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

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

4 fields of ValidationReport
entry_count integer · minimum 0 required

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

error_count integer · minimum 0 required

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

warning_count integer · minimum 0 required

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

entries array of object optional

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

4 fields
index integer · minimum 1 required

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

severity string required

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

errorwarning
code string required

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

detail string required

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

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition_import",
  "livemode": true,
  "mocked": true,
  "status": "validating",
  "validation_report": {
    "entry_count": 0,
    "error_count": 0,
    "warning_count": 0,
    "entries": [
      {
        "index": 1,
        "severity": "error",
        "code": "no_payee_destination",
        "detail": "The payee has no verified destination, so this line cannot be paid."
      }
    ]
  },
  "requisition_ids": [
    "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  ]
}
GET/v1/sourcing/imports/{import_id}#

Retrieve a bulk upload

sourcing.imports.retrieve

Returns your upload with the report naming every row that failed and why.

Path parameters

import_id string required

The upload's identifier, starting with flow_enterprise_requisition_: the id returned by POST /v1/sourcing/imports when you uploaded the file.

Returns

The import.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

Where 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_acceptanceacceptedrejected
requisition_ids array of string optional

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

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

4 fields of ValidationReport
entry_count integer · minimum 0 required

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

error_count integer · minimum 0 required

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

warning_count integer · minimum 0 required

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

entries array of object optional

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

4 fields
index integer · minimum 1 required

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

severity string required

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

errorwarning
code string required

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

detail string required

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

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition_import",
  "livemode": true,
  "mocked": true,
  "status": "validating",
  "validation_report": {
    "entry_count": 0,
    "error_count": 0,
    "warning_count": 0,
    "entries": [
      {
        "index": 1,
        "severity": "error",
        "code": "no_payee_destination",
        "detail": "The payee has no verified destination, so this line cannot be paid."
      }
    ]
  },
  "requisition_ids": [
    "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  ]
}
GET/v1/sourcing/taxonomies/{taxonomy_id}#

Retrieve an occupation taxonomy

sourcing.taxonomies.retrieve

Read how local and colloquial job titles map to occupation codes, so a requisition for a gateman and one for a security officer reach the same people.

The taxonomy is versioned. What a title means changes, and your requisition keeps meaning what it meant.

Path parameters

taxonomy_id string required

The 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 taxonomy.

id string required

The 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" required

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

version string required

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

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

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

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

Local and colloquial titles that mean this occupation.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "occupation_taxonomy",
  "version": "2026.08.1",
  "entries": [
    {
      "occupation_code": "ng-7412",
      "canonical_title": "example",
      "aliases": [
        "example"
      ]
    }
  ]
}
GET/v1/sourcing/channels#

List the channels available to you

sourcing.channels.list

Formal boards, professional networks, alumni portals, campus nodes and the Droomwork talent pool, which is a channel like any other.

Each entry tells you the category, the cost model and your own rolling yield. It does not name the operator behind a channel.

Query parameters

limit integer optional

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

category string optional

Only 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_pool
paid boolean optional

Pass 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

A page of channels.

object always "list" required

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

data array of Channel required

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

10 fields of Channel
id string required

The 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" required

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

category string required

The category, not the operator. Which platforms sit behind a category is not disclosed.

formal_boardprofessional_networkalumni_portalcampus_nodetalent_pool
label string optional

A name for your own reporting. Not the operator behind the channel.

paid boolean required

true 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 optional

How this channel charges, so a spend ceiling can be reasoned about.

2 fields
basis string optional

What 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_leadfree
indicative_unit_cost Money optional
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

quota object required

How 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 optional

The most posts this channel accepts from you in one day. null where no daily cap applies.

remaining_today integer · nullable optional

How 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 optional

Your own yield on this channel. Never anyone else's.

2 fields
qualified_lead_rate number · nullable optional

The 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 optional

How many days the rolling yield is measured over, counted back from today. qualified_lead_rate covers exactly this window.

credentials_held boolean optional

true 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 optional

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

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), 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)
Response
{
  "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": 1234567,
          "currency": "NGN"
        }
      },
      "rolling_yield": {
        "qualified_lead_rate": 1,
        "window_days": 1
      },
      "credentials_held": true,
      "available": true
    }
  ],
  "has_more": true
}
GET/v1/sourcing/channels/{channel_id}#

Retrieve a channel

sourcing.channels.retrieve

Returns the channel with its cost model, quota and your rolling yield on it.

Path parameters

channel_id string required

The 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

The channel.

id string required

The 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" required

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

category string required

The category, not the operator. Which platforms sit behind a category is not disclosed.

formal_boardprofessional_networkalumni_portalcampus_nodetalent_pool
label string optional

A name for your own reporting. Not the operator behind the channel.

paid boolean required

true 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 optional

How this channel charges, so a spend ceiling can be reasoned about.

2 fields
basis string optional

What 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_leadfree
indicative_unit_cost Money optional
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

quota object required

How 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 optional

The most posts this channel accepts from you in one day. null where no daily cap applies.

remaining_today integer · nullable optional

How 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 optional

Your own yield on this channel. Never anyone else's.

2 fields
qualified_lead_rate number · nullable optional

The 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 optional

How many days the rolling yield is measured over, counted back from today. qualified_lead_rate covers exactly this window.

credentials_held boolean optional

true 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 optional

true 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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": 1234567,
      "currency": "NGN"
    }
  },
  "rolling_yield": {
    "qualified_lead_rate": 1,
    "window_days": 1
  },
  "credentials_held": true,
  "available": true
}
GET/v1/sourcing/requisitions/{requisition_id}/artefacts#

List the generated channel artefacts

sourcing.artefacts.list

Your requisition produces a full set of channel copy, one piece sized and shaped for each destination. Preview and edit it here before anything goes out.

Path parameters

requisition_id string required

The 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 optional

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

Returns

A page of artefacts.

object always "list" required

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

data array of Artefact required

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

7 fields of Artefact
id string required

The 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" required

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

channel_id string required

The 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 optional

The title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.

body string required

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

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

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

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

Which 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_claimrequirement
in_requisition string required

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

The value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.

agrees boolean required

true 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 optional

When this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.

editable boolean optional

False once the broadcast has gone out on this channel.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional)
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)
Response
{
  "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
}
GET/v1/sourcing/requisitions/{requisition_id}/artefacts/{artefact_id}#

Retrieve an artefact

sourcing.artefacts.retrieve

Returns the copy with the result of checking it against your requisition.

Pay figures, employer legal names, statutory claims and requirements are compared exactly. A mismatch fails the render, and the failure is recorded here rather than corrected silently.

Path parameters

requisition_id string required

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

The 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

The artefact.

id string required

The 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" required

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

channel_id string required

The 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 optional

The title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.

body string required

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

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

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

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

Which 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_claimrequirement
in_requisition string required

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

The value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.

agrees boolean required

true 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 optional

When this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.

editable boolean optional

False once the broadcast has gone out on this channel.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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
}
PATCH/v1/sourcing/requisitions/{requisition_id}/artefacts/{artefact_id}#

Edit an artefact before broadcast

sourcing.artefacts.update

Your edit is checked against the requisition the same way the generated copy is. You can't introduce a pay figure the requisition doesn't carry.

Path parameters

requisition_id string required

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

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

headline string optional

The 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 optional

The 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 updated artefact.

id string required

The 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" required

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

channel_id string required

The 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 optional

The title line of the copy as it will appear on this channel. You can change it with PATCH while editable is true.

body string required

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

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

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

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

Which 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_claimrequirement
in_requisition string required

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

The value as the copy says it. Where it differs from in_requisition, agrees is false and the copy fails.

agrees boolean required

true 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 optional

When this copy last failed the check, as an RFC 3339 timestamp in UTC. null when it has never failed.

editable boolean optional

False once the broadcast has gone out on this channel.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X 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)
Response
{
  "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
}
POST/v1/sourcing/requisitions/{requisition_id}/broadcasts#

Broadcast a requisition across every configured channel

sourcing.broadcasts.create

The broadcast is accepted and then runs, each channel independent of the others. A channel that fails is retried on its own schedule, and when the retries run out it reaches a terminal failure you are told about. No channel is ever dropped quietly.

Your spend ceilings are checked first. If a paid channel would breach one, the broadcast is refused and no charge is calculated or debited.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body optional

channel_ids array of string optional

Defaults to every channel configured for your organisation.

Returns

Broadcast accepted. Watch the deliveries.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

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

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

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

The 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 optional

When 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

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "broadcast",
  "livemode": true,
  "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"
}
GET/v1/sourcing/broadcasts/{broadcast_id}#

Retrieve a broadcast

sourcing.broadcasts.retrieve

Returns the broadcast with the state of every channel it went to.

Path parameters

broadcast_id string required

The 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

The broadcast.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

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

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

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

The 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 optional

When 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_broadcast_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "broadcast",
  "livemode": true,
  "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"
}
POST/v1/sourcing/broadcasts/{broadcast_id}/deliveries/{delivery_id}/retry#

Retry a failed delivery

sourcing.deliveries.retry

Retries one channel without touching the others.

Path parameters

broadcast_id string required

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

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

Retry accepted.

id string required

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

The 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "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"
}
GET/v1/sourcing/deliveries#

List deliveries

sourcing.deliveries.list

One delivery per channel per broadcast, each with its own state, attempts and terminal outcome. Filter by broadcast to see one send, or leave it off to find every failure across your requisitions.

A failed channel is always reported rather than dropped, and this is where you find it.

Query parameters

limit integer optional

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

starting_after string optional

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

broadcast_id string optional

One 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 optional

Only 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 optional

Restricts 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.

queuedsendingdeliveredretryingfailed

Returns

A page of deliveries.

object always "list" required

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

data array of Delivery required

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

10 fields of Delivery
id string required

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

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

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), 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)
Response
{
  "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
}
POST/v1/sourcing/deliveries#

Record a channel delivery

sourcing.deliveries.create

Deliveries normally appear when a broadcast runs. Record one directly when you posted by hand on a channel we can't reach, so that posting counts in attribution like the rest.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body optional

broadcast_id string optional

The 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 optional

The 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 delivery.

id string required

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

The 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

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "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"
}
GET/v1/sourcing/spend_ceilings#

Retrieve your spend ceilings

sourcing.spend_ceilings.retrieve

A ceiling per requisition and a ceiling for your organisation. Both are checked before any paid channel runs.

When one would be breached, the broadcast is blocked and no charge is computed. The ceilings and what you've consumed come from your wallet.

Returns

The ceilings and what has been consumed.

object always "spend_ceilings" required

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

organisation object required

Your 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 required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

consumed Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

per_requisition array of object optional

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

The 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 required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

consumed Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "object": "spend_ceilings",
  "organisation": {
    "ceiling": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "consumed": {
      "amount": 1234567,
      "currency": "NGN"
    }
  },
  "per_requisition": [
    {
      "requisition_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "ceiling": {
        "amount": 1234567,
        "currency": "NGN"
      },
      "consumed": {
        "amount": 1234567,
        "currency": "NGN"
      }
    }
  ]
}
POST/v1/sourcing/requisitions/{requisition_id}/capture_links#

Create a capture link for a channel

sourcing.capture_links.create

The link a candidate follows. Its token is opaque and can't be enumerated, and no personally identifying data ever appears in a query parameter. A link gets pasted into places you don't control.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

channel_id string required

The 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 optional

When the link stops working, as an RFC 3339 timestamp in UTC. Omit it for a link with no expiry.

Returns

The capture link.

id string required

The 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" required

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

requisition_id string required

The 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 optional

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

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

Opaque and not enumerable. Guessing a neighbouring one finds nothing.

leads_captured integer · minimum 0 optional

How many leads have come in through this link so far. 0 for a link nobody has followed yet.

expires_at string · date-time · nullable optional

When the link stops working, as an RFC 3339 timestamp in UTC. null when the link has no expiry.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
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)
Response
{
  "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"
}
GET/v1/sourcing/capture_links/{capture_link_id}#

Retrieve a capture link

sourcing.capture_links.retrieve

Returns the link and how many leads it produced.

Path parameters

capture_link_id string required

The 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 capture link.

id string required

The 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" required

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

requisition_id string required

The 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 optional

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

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

Opaque and not enumerable. Guessing a neighbouring one finds nothing.

leads_captured integer · minimum 0 optional

How many leads have come in through this link so far. 0 for a link nobody has followed yet.

expires_at string · date-time · nullable optional

When the link stops working, as an RFC 3339 timestamp in UTC. null when the link has no expiry.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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"
}
GET/v1/sourcing/requisitions/{requisition_id}/knockout_rules#

List the knockout rules for a requisition

sourcing.knockout_rules.list

The rules that decide whether a candidate passes or fails. They're deterministic, versioned, readable here, and the only thing that decides an outcome.

The screening conversation extracts a meaning from each answer. It never decides whether the answer passes; these rules do.

Path parameters

requisition_id string required

The 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

The knockout rules.

object always "list" required

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

data array of KnockoutRule required

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

5 fields of KnockoutRule
code string required

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

What the candidate is asked. The conversation may phrase it differently, but the answer is judged by rule, never by the phrasing.

rule string required

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

The 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 optional

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

Five by default. A screening conversation that runs longer than this stops being screening and starts being an interview.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "object": "list",
  "data": [
    {
      "code": "no_payee_destination",
      "question": "example",
      "rule": "years_experience >= 3",
      "rule_pack_version": "2026.08.1",
      "mandatory": true
    }
  ],
  "question_cap": 1
}
GET/v1/sourcing/leads#

List leads

sourcing.leads.list

Your leads. Deduplicated within a requisition by contact identity, so the same person answering twice is one lead.

Query parameters

limit integer optional

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

starting_after string optional

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

requisition_id string optional

Restricts 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 optional

Restricts 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_progresspassedfailedabandoned

Returns

A page of leads.

object always "list" required

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

data array of Lead required

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

14 fields of Lead
id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

Which version produced this lead. A shortlist is reproducible against it.

channel_id string optional

The 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 optional
in_progresspassedfailedabandoned
fit_score number · minimum 0 · maximum 100 required

How 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 optional

The three factors that most influenced the score. Three, always, so a score is never a number without a reason.

2 fields
factor string required

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

How much this factor moved fit_score. Read the three together to see why the lead scored as it did.

consent LeadConsent required

Captured at first contact, before anything else is asked.

4 fields of LeadConsent
lawful_basis string required

The 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_interestcontract
channel string required

The channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.

wording_version string required

The exact wording presented. Answered from the record, not the current page.

captured_at string · date-time required

When the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.

duplicate_of string · nullable optional

Set when this response matched an existing lead on contact identity.

promoted_subject_ref string · nullable optional

Set 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 optional

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

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), 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)
Response
{
  "object": "list",
  "data": [
    {
      "id": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "lead",
      "livemode": true,
      "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
}
POST/v1/sourcing/leads#

Record a lead

sourcing.leads.create

Record a candidate who came in through a channel. Consent is captured with the lead, never assumed from the channel: the channel agreed to nothing on the candidate's behalf.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body optional

requisition_id string optional

The 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 optional

The 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.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

Which version produced this lead. A shortlist is reproducible against it.

channel_id string optional

The 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 optional
in_progresspassedfailedabandoned
fit_score number · minimum 0 · maximum 100 required

How 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 optional

The three factors that most influenced the score. Three, always, so a score is never a number without a reason.

2 fields
factor string required

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

How much this factor moved fit_score. Read the three together to see why the lead scored as it did.

consent LeadConsent required

Captured at first contact, before anything else is asked.

4 fields of LeadConsent
lawful_basis string required

The 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_interestcontract
channel string required

The channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.

wording_version string required

The exact wording presented. Answered from the record, not the current page.

captured_at string · date-time required

When the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.

duplicate_of string · nullable optional

Set when this response matched an existing lead on contact identity.

promoted_subject_ref string · nullable optional

Set 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 optional

When 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

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "lead",
  "livemode": true,
  "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"
}
GET/v1/sourcing/leads/{lead_id}#

Retrieve a lead

sourcing.leads.retrieve

Returns the lead with its fit score, the three factors that most influenced it, and the requisition version it was captured against.

The version travels with the lead, so you can reproduce a shortlist for a given version.

Path parameters

lead_id string required

The 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 lead.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

Which version produced this lead. A shortlist is reproducible against it.

channel_id string optional

The 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 optional
in_progresspassedfailedabandoned
fit_score number · minimum 0 · maximum 100 required

How 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 optional

The three factors that most influenced the score. Three, always, so a score is never a number without a reason.

2 fields
factor string required

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

How much this factor moved fit_score. Read the three together to see why the lead scored as it did.

consent LeadConsent required

Captured at first contact, before anything else is asked.

4 fields of LeadConsent
lawful_basis string required

The 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_interestcontract
channel string required

The channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.

wording_version string required

The exact wording presented. Answered from the record, not the current page.

captured_at string · date-time required

When the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.

duplicate_of string · nullable optional

Set when this response matched an existing lead on contact identity.

promoted_subject_ref string · nullable optional

Set 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 optional

When 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "lead",
  "livemode": true,
  "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"
}
GET/v1/sourcing/leads/{lead_id}/screening#

Retrieve the screening conversation record

sourcing.screenings.retrieve

Shows you the two halves separately: what was extracted from each answer, and what the rules then decided.

That lets you tell a bad extraction from a bad rule. They're different problems with different fixes.

Path parameters

lead_id string required

The 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 screening record.

object always "screening" required

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

lead_id string required

The 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 required
in_progresspassedfailedabandoned
surface string required

Where 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.

whatsappweb
exchanges array of object required

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

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

How the candidate answered: text (typed) or voice (spoken). Either way, extracted holds what was taken from the answer and the same rule judges it.

textvoice
extracted object required

What was extracted from the candidate's answer. It decides nothing.

3 fields
field string optional

The fact the answer was read for, named as your rule names it: years_experience for a rule of years_experience >= 3.

value string optional

What 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 optional

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

The decision on the answer, and the rule that made it. This is the only thing that decides.

3 fields
rule string required

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

true 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 optional

The 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" optional

Always rule_pack. The decision comes from your knockout rules, never from the conversation.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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"
}
POST/v1/sourcing/leads/{lead_id}/promote#

Promote a lead toward identity

sourcing.leads.promote

Hands the lead on for identity resolution. Attribution survives promotion, so you can still see which channel produced this person months afterwards.

Path parameters

lead_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The promoted lead.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

requisition_id string required

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

Which version produced this lead. A shortlist is reproducible against it.

channel_id string optional

The 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 optional
in_progresspassedfailedabandoned
fit_score number · minimum 0 · maximum 100 required

How 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 optional

The three factors that most influenced the score. Three, always, so a score is never a number without a reason.

2 fields
factor string required

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

How much this factor moved fit_score. Read the three together to see why the lead scored as it did.

consent LeadConsent required

Captured at first contact, before anything else is asked.

4 fields of LeadConsent
lawful_basis string required

The 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_interestcontract
channel string required

The channel the candidate gave consent on. The channel agreed to nothing on their behalf; this is where the candidate themselves agreed.

wording_version string required

The exact wording presented. Answered from the record, not the current page.

captured_at string · date-time required

When the candidate gave consent, as an RFC 3339 timestamp in UTC. It comes before every answer the lead gave.

duplicate_of string · nullable optional

Set when this response matched an existing lead on contact identity.

promoted_subject_ref string · nullable optional

Set 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 optional

When 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_lead_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "lead",
  "livemode": true,
  "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"
}
GET/v1/sourcing/leads/{lead_id}/attribution#

Retrieve the attribution record for a lead

sourcing.attributions.retrieve

First touch, last touch and the referring party. It can't change once written, and it survives promotion, so you can ask which channel produced a hire long after the hire.

Path parameters

lead_id string required

The 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 attribution record.

object always "attribution" required

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

lead_id string required

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

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

The 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 optional

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

When the first contact happened, as an RFC 3339 timestamp in UTC.

last_touch object required

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

The 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 optional

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

When the latest contact happened, as an RFC 3339 timestamp in UTC.

referring_party string · nullable optional

Who 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 optional

Always 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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
}
GET/v1/sourcing/analytics#

Retrieve channel yield analytics

sourcing.analytics.retrieve

Leads captured, qualified, promoted and hired per channel, with cost per qualified lead.

Your organisation only. You see your own yield on a channel and never anyone else's.

Query parameters

requisition_id string optional

Restricts 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 optional

The 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 optional

The 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

The analytics.

object always "channel_analytics" required

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

scope always "requesting_organisation" required

Your own figures only. Never another organisation's yield on a channel.

channels array of object required

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

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

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

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

How many of those leads you promoted toward identity in the period, with POST /v1/sourcing/leads/{lead_id}/promote.

hired integer required

How 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 optional
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: requisition_id (optional), from (optional), to (optional)
curl -X GET "https://sandbox.droomwork.io/v1/sourcing/analytics?requisition_id=flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&from=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)" \
  -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: 'Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)', to: 'Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)' });
// 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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)', {
  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='Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)', to='Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)')
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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)',
    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: 'Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)', to: 'Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)');
<?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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)');
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", "Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)", "Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)");
// 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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)"))
    .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: "Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)", to: "Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)");
// 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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)");
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("Thu Jan 01 2026 01:00:00 GMT+0100 (West Africa Time)").To("Fri Jan 01 2027 01:00:00 GMT+0100 (West Africa Time)").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=Thu%20Jan%2001%202026%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)&to=Fri%20Jan%2001%202027%2001%3A00%3A00%20GMT%2B0100%20(West%20Africa%20Time)", nil)
req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "channel_analytics",
  "scope": "requesting_organisation",
  "channels": [
    {
      "channel_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "captured": 1,
      "qualified": 1,
      "promoted": 1,
      "hired": 1,
      "cost_per_qualified_lead": {
        "amount": 1234567,
        "currency": "NGN"
      }
    }
  ]
}
GET/v1/sourcing/readiness#

What FLOW needs, what you have, and what is missing

sourcing.readiness.retrieve

You don't need another module set up before FLOW, so this reports what FLOW itself needs: channel credentials and wallet funding.

Returns

The readiness report.

object always "readiness_report" required

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

module string required

The module whose readiness endpoint you called, such as GET /v1/payroll/readiness: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance) or route (payouts).

anchorproofrailflowmatchrunremitroute
mode string required

integrated means a required fact comes from the Droomwork module that owns it; standalone means you supply it yourself under an attestation. The checks are the same in both, and what each fact is worth is recorded on its row as held.

integratedstandalone
ready boolean required

true when every row is satisfied and the module has what it needs from you. false when a required fact is missing or held at too low an assurance; rows says which.

rows array of ReadinessRow required

One row per fact the module requires: who owns it, the assurance it needs, what you hold and whether that satisfies it. The rows that are not satisfied are what to bring.

8 fields of ReadinessRow
fact string required

Named for what it is, not for who supplies it.

owner string required

The sibling module that owns this fact when running integrated.

contract_row string optional
required string required

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
held one of required
Assuranceor
source string · nullable optional
integratedstandalonenull
satisfied boolean required
missing_because string · nullable optional
not_suppliedassurance_too_lownull
always_enforced array of string optional

Gates that hold in every mode and cannot be attested away. Consent before any source is queried, bank account validation, the tax identifiers returns are filed under, and duplicate detection within the organisation's own population.

channels_without_credentials array of string optional

The 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 optional

Paid channels cannot run without it, and the ceiling check refuses first.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "object": "readiness_report",
  "module": "anchor",
  "mode": "integrated",
  "ready": true,
  "rows": [
    {
      "fact": "anchored_subject",
      "owner": "anchor",
      "required": "attested",
      "held": "attested",
      "satisfied": true,
      "contract_row": "6.1.13",
      "source": "integrated",
      "missing_because": "not_supplied"
    }
  ],
  "always_enforced": [
    "example"
  ],
  "channels_without_credentials": [
    "example"
  ],
  "wallet_funded": true
}
GET/v1/sourcing/deliveries/{delivery_id}#

Retrieve a channel delivery

sourcing.deliveries.retrieve

What one channel did with one broadcast. A delivery the channel refused says so. A broadcast that reached four of five channels didn't work.

Path parameters

delivery_id string required

The 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

The delivery.

id string required

The 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" required

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

broadcast_id string required

The 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 optional

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

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

failed is terminal and always reported. A channel you selected is never dropped silently.

queuedsendingdeliveredretryingfailed
attempts integer · minimum 0 required

How 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 optional

Exponential backoff. Absent once the state is terminal.

last_fault string · nullable optional

Why 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 optional

The 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "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"
}
POST/v1/sourcing/requisitions/{requisition_id}/quarantine#

Quarantine a requisition

sourcing.requisitions.quarantine

Holds a requisition that can't be published as written, with the findings that stopped it. Quarantine isn't rejection: the findings tell you what to fix.

Path parameters

requisition_id string required

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

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The quarantined requisition.

id string required

The 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" required

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

livemode boolean required

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

mocked boolean required

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

status string required

A 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_reviewquarantinedpublishedclosed
version integer · minimum 1 required

An edit after first broadcast makes a new version. Leads stay on theirs.

job_title string required

The 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 optional

The 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 optional
permanentfixed_termcontractcasualapprenticeship
headcount integer · minimum 1 required

How 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 optional
3 fields of Compensation
amount Money required
2 fields of Money
amount integer · int64 required

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

currency string required

ISO 4217 code.

period string required

The span the amount pays for: monthly, annual, daily or per_task (one payment for each task done). Required whenever you send compensation.

monthlyannualdailyper_task
negotiable boolean optional

Whether the figure is open to negotiation: true when it is, false when it is fixed.

location Location optional
3 fields of Location
description string required

Where the work is, in words a candidate would recognise, such as Ikeja, Lagos. The one part of a location you must give.

state string optional

The Nigerian state the work is in, such as Lagos. Optional; the description alone is enough to create the requisition.

geofence_radius_metres integer · nullable optional

A 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 optional

The 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 optional

Fields 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 optional

Why 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.

QuarantineFindingor
created_at string · date-time optional

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

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "flow_enterprise_requisition_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "requisition",
  "livemode": true,
  "mocked": true,
  "status": "pending_confirmation",
  "version": 1,
  "job_title": "example",
  "headcount": 1,
  "occupation_code": "ng-7412",
  "employment_type": "permanent",
  "compensation": {
    "amount": {
      "amount": 1234567,
      "currency": "NGN"
    },
    "period": "monthly",
    "negotiable": true
  },
  "location": {
    "description": "example",
    "state": "example",
    "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"
}
GET/v1/sourcing/events#

List events

sourcing.events.list

Every event FLOW has recorded for you, oldest first, and nothing is ever removed. Every event here is one the webhook catalogue declares, so the code you wrote to handle a webhook delivery handles a replay too.

Two ways to read it. Pass stream with after to replay one stream from the sequence you last handled; that is exact and needs no cursor, because the sequence counts per organisation and per stream and is the only ordering you can rely on. Without stream, you get events across streams in the order they were recorded, paged with starting_after.

Query parameters

stream string optional

The id of the record whose 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 optional

The 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 optional

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

starting_after string optional

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

Returns

A page of events.

object always "list" required

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

data array of StoredEvent required

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

13 fields of StoredEvent
id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Droomwork is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

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

stream string required

The id of the record this event is about, such as a 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 required

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

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "object": "list",
  "data": [
    {
      "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "type": "run.payslip.calculated",
      "schema_version": 1,
      "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "sequence": 0,
      "occurred_at": "2026-09-01T09:00:00Z",
      "livemode": true,
      "mocked": true,
      "source": "anchor",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "event",
      "stream": "example",
      "data": {}
    }
  ],
  "has_more": true
}
GET/v1/sourcing/events/{event_id}#

Retrieve an event

sourcing.events.retrieve

Returns one event. An identifier belonging to another organisation comes back as not found, never as refused, so you can't learn whether it exists.

Path parameters

event_id string required

The 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 event.

id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Droomwork is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

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

stream string required

The id of the record this event is about, such as a 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 required

The 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

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "type": "run.payslip.calculated",
  "schema_version": 1,
  "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "sequence": 0,
  "occurred_at": "2026-09-01T09:00:00Z",
  "livemode": true,
  "mocked": true,
  "source": "anchor",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "event",
  "stream": "example",
  "data": {}
}
GET/v1/sourcing/audit_entries#

List audit entries

sourcing.audit_entries.list

Who did what in your organisation, newest first. One row per attempt, not per success: a refusal is recorded too, and repeated forbidden answers on one credential is what an attack looks like from the inside.

A read that succeeded is not recorded. A log holding every list call is mostly noise.

Query parameters

action string optional

The 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 optional

The 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 optional

The 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 optional

The 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 optional

One 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.

succeededrefusedfailed
recorded_after string optional

Only 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 optional

Only 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 optional

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

starting_after string optional

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

Returns

A page of audit entries.

object always "list" required

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

data array of AuditEntry required

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

13 fields of AuditEntry
id string required

The entry's identifier. It starts with audit_entry_ and never changes; pass it to GET /v1/sourcing/audit_entries/{audit_entry_id} to read this entry again, or as starting_after to page past it.

object always "audit_entry" required

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

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It 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 required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list is ordered by it, newest first.

request_id string required

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

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

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

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record the attempt was aimed at, as the collection name in the route, such as requisitions or leads. null when the call reached no route at all.

resource_id string · nullable optional

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

How 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.

succeededrefusedfailed
status integer required

The HTTP status the caller was given, such as 201 or 403. It is what outcome is read from.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "object": "list",
  "data": [
    {
      "id": "audit_entry_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "audit_entry",
      "livemode": true,
      "mocked": true,
      "at": "2026-09-01T09:00:00Z",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "actor_type": "example",
      "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "action": "example",
      "outcome": "succeeded",
      "status": 1,
      "resource": "example",
      "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
  ],
  "has_more": true
}
GET/v1/sourcing/audit_entries/{audit_entry_id}#

Retrieve an audit entry

sourcing.audit_entries.retrieve

Returns one entry. An identifier belonging to another organisation comes back as not found, never as refused, so you can't learn whether it exists.

Path parameters

audit_entry_id string required

The entry's id, as it reads on an entry you listed at GET /v1/sourcing/audit_entries. It starts with audit_entry_.

Returns

The audit entry.

id string required

The 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" required

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

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It 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 required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list is ordered by it, newest first.

request_id string required

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

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

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

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record the attempt was aimed at, as the collection name in the route, such as requisitions or leads. null when the call reached no route at all.

resource_id string · nullable optional

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

How 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.

succeededrefusedfailed
status integer required

The HTTP status the caller was given, such as 201 or 403. It is what outcome is read from.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/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)
Response
{
  "id": "audit_entry_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "audit_entry",
  "livemode": true,
  "mocked": true,
  "at": "2026-09-01T09:00:00Z",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "actor_type": "example",
  "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "action": "example",
  "outcome": "succeeded",
  "status": 1,
  "resource": "example",
  "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}