Skip to main content

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​

FieldPresenceNotes
rulesetIdrequiredAn 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.
inputrequiredYour structured transactional data — any JSON value the ruleset's declared input schema accepts.
documentsoptionalRole-keyed document sources — see below. Omit entirely for a document-free job.
testoptional, default falseRuns the SAME ruleset logic and sends the SAME terminal webhook event, with test: true set and no business-data mutation.
localeoptionalde for German check messages or en for English. Omission uses the tenant's configured default at submission.
bypassAiCacheoptional, default falseWhen true, every extract step of the run skips the AI result cache read. See Skipping the AI result cache.
webhookDestinationIdoptionalOverrides 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​

CodeWhen
VALIDATION_FAILEDThe request body doesn't satisfy the schema (wrong types, a flat documents role, an extra field).
DOCUMENT_BASE64_INVALIDAn inline document's contentBase64 isn't valid canonical base64.
DOCUMENT_MEDIA_TYPE_UNSUPPORTED / DOCUMENT_MEDIA_TYPE_MISMATCHAn inline document's declared media type is unsupported, or doesn't match its bytes.
DOCUMENT_BYTES_TOO_LARGE / DOCUMENT_COUNT_EXCEEDED / DOCUMENT_TOTAL_BYTES_EXCEEDEDA per-document, per-request-count, or aggregate ceiling was exceeded.
NOT_FOUNDrulesetId 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 / FORBIDDENMissing, 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.