Submit a job
POST /jobs accepts your structured input, optionally with documents, and
queues one run. It always returns 202 Accepted with the new run's id
immediately — a job is never evaluated synchronously in the request. Poll
for the outcome (see Poll a run) or register a webhook
destination to be notified when it terminates.
Submitting a job requires the submit scope.
Request shape
| Field | Presence | Notes |
|---|---|---|
rulesetId | required | An activated version handle of the ruleset to run. The job executes that ruleset's active version at submission and reports both version ids when polled. |
input | required | Your structured transactional data — any JSON value the ruleset's declared input schema accepts. |
documents | optional | Role-keyed document sources — see below. Omit entirely for a document-free job. |
test | optional, default false | Runs the SAME ruleset logic and sends the SAME terminal webhook event, with test: true set and no business-data mutation. |
locale | optional | de for German check messages or en for English. Omission uses the tenant's configured default at submission. |
bypassAiCache | optional, default false | When true, every extract step of the run skips the AI result cache read. See Skipping the AI result cache. |
webhookDestinationId | optional | Overrides the ruleset's default webhook destination for this one job. Requires the webhook_destination_override scope. |
Choosing the result language and version
Set locale: 'de' or locale: 'en' to choose the language of check messages.
Omitting locale uses the tenant's configured default. The language is frozen
when the job is submitted, including when the job carries documents; later
changes to the tenant default do not change this run. Any other value is
refused with VALIDATION_FAILED.
You may name any version of your ruleset that has been activated. The job runs
the ruleset's active version at submission, which can differ from the handle
you supplied after an approval or rollback. Poll a run
explains rulesetVersionId, requestedRulesetVersionId, and the frozen locale.
The exact result bytes carry the language in Rule-Line-Result-Locale.
The documents shape is locked
documents is keyed by the ruleset's own declared role names (for example
invoice, receipts). Every role's value is an array, even a role whose
contract permits exactly one document — there is no flat, non-array form,
and a flat object under a role is refused before the job is ever queued:
{
"documents": {
"invoice": [{ "documentId": "33333333-3333-4333-8333-333333333333" }],
"receipts": [
{ "documentId": "33333333-3333-4333-8333-333333333333" },
{ "inline": { "contentBase64": "aGVsbG8=", "mediaType": "application/pdf" } }
]
}
}
Each array entry is one of exactly two shapes — never both keys at once:
{ "documentId": "..." }— a reference to a document you already uploaded (see Upload a document).{ "inline": { "contentBase64": "...", "mediaType": "...", "originalFilename": "..." } }— bytes submitted directly in this request. An inline document becomes an immutable run-scoped snapshot; it does not silently become a reusable document you can reference from another job.
You never need to extract embedded content yourself: a submitted container
document's immediate embedded attachments — for example the CII XML inside a
ZUGFeRD PDF/A-3 invoice — are extracted automatically at intake and become
available to the ruleset as ordinary nested inputs. This happens for either
document source (documentId or inline) and requires nothing extra in the
request body above.
Array order is preserved and meaningful — a ruleset expression can index
into a role's array (documents.receipts[0]), so never reorder entries
when retrying a submission.
SDK example
import { client, submitJob } from '@one-unity/rule-line-sdk';
client.setConfig({
baseUrl: 'https://api.rule-line.example.com',
auth: 'rl_key_your_api_key',
});
const result = await submitJob({
body: {
rulesetId: '11111111-1111-4111-8111-111111111111',
input: { amount: 249.5, currency: 'EUR' },
documents: {
invoice: [{ documentId: '33333333-3333-4333-8333-333333333333' }],
},
test: false,
locale: 'de',
},
});
if (result.error) {
throw new Error(`submission failed: ${result.error.code}`);
}
console.log(`queued run ${result.data.id}`);
Equivalent raw HTTP
POST /jobs HTTP/1.1
Host: api.rule-line.example.com
Authorization: Bearer rl_key_your_api_key
Content-Type: application/json
{
"rulesetId": "11111111-1111-4111-8111-111111111111",
"input": { "amount": 249.5, "currency": "EUR" },
"documents": {
"invoice": [{ "documentId": "33333333-3333-4333-8333-333333333333" }]
},
"test": false,
"locale": "de"
}
Expected response
{
"id": "22222222-2222-4222-8222-222222222222",
"status": "queued"
}
The returned id is the run's identity for every later call — polling
(GET /jobs/{id}), reading the result (GET /jobs/{id}/result), and its
row in run history (GET /runs) all key off this same id.
Test runs
Setting test: true runs the exact same ruleset logic and produces the
same terminal webhook event shape, with test: true set on the event —
useful for verifying an integration end to end without touching production
data. It still counts as a real run for polling and history purposes.
Skipping the AI result cache
An extract step normally reuses a cached AI result when it has already read
the same document under the same settings. Setting bypassAiCache: true
makes every extract step of this run skip that cache read, so the run asks the
model again. Each fresh result is written as the newest cache entry for its
key, which later runs without the flag then reuse. Omitting the field means
false.
{
"rulesetId": "11111111-1111-4111-8111-111111111111",
"input": { "amount": 249.5 },
"bypassAiCache": true
}
A call made because of the flag is metered like any other call, so use it for
the runs that need a fresh reading rather than as a default. The field must be
a JSON boolean: "true" and 1 are refused with
VALIDATION_FAILED. A ruleset
without an extract step is not affected by the flag. See
Contract changes for when
the field was added.
Overriding the webhook destination
Every ruleset version names its own default webhook destination. Naming a
different, already-registered destination for one job requires the
webhook_destination_override scope — a job body can only ever carry an
existing destination's id, never a raw URL:
{
"rulesetId": "11111111-1111-4111-8111-111111111111",
"input": { "amount": 249.5 },
"webhookDestinationId": "44444444-4444-4444-8444-444444444444",
"test": false
}
A key without webhook_destination_override that supplies
webhookDestinationId at all is refused with
FORBIDDEN before the run is
created.
Errors
| Code | When |
|---|---|
VALIDATION_FAILED | The request body doesn't satisfy the schema (wrong types, a flat documents role, an extra field). |
DOCUMENT_BASE64_INVALID | An inline document's contentBase64 isn't valid canonical base64. |
DOCUMENT_MEDIA_TYPE_UNSUPPORTED / DOCUMENT_MEDIA_TYPE_MISMATCH | An inline document's declared media type is unsupported, or doesn't match its bytes. |
DOCUMENT_BYTES_TOO_LARGE / DOCUMENT_COUNT_EXCEEDED / DOCUMENT_TOTAL_BYTES_EXCEEDED | A per-document, per-request-count, or aggregate ceiling was exceeded. |
NOT_FOUND | rulesetId doesn't name an ACTIVATED version of the caller's own tenant — an unknown id, an id belonging to another tenant, a working version that was never activated, and a version whose activation was cleared by a later edit all return this SAME 404. Also returned when a referenced documentId or webhookDestinationId doesn't resolve for this tenant. |
AUTHENTICATION_REQUIRED / FORBIDDEN | Missing, invalid, or under-scoped credentials — including a webhookDestinationId supplied without the override scope. |
See the full POST /jobs reference
for the complete request/response schema.