Skip to main content

Poll a run

GET /jobs/{id} returns a run's current lifecycle status and, once terminal, its immutable outcome. Polling requires the read scope and performs no mutation — poll as often as you need while a run is queued/active.

Checking status​

import { client, pollJob } from '@one-unity/rule-line-sdk';

client.setConfig({
baseUrl: 'https://api.rule-line.example.com',
auth: 'rl_key_your_api_key',
});

const result = await pollJob({ path: { id: '22222222-2222-4222-8222-222222222222' } });
if (result.error) {
// A run erased under the retention policy answers 410 with a tombstone
// acknowledgement — deliberately NOT the standard problem envelope, so it
// carries no `code`. Narrow before reading one.
if ('code' in result.error) {
throw new Error(`poll failed: ${result.error.code}`);
}
throw new Error(`run ${result.error.id} was erased at ${result.error.erasedAt}`);
}
if (result.data.status === 'succeeded' || result.data.status === 'failed') {
console.log(`terminal: ${result.data.outcome}`);
}
GET /jobs/22222222-2222-4222-8222-222222222222 HTTP/1.1
Host: api.rule-line.example.com
Authorization: Bearer rl_key_your_api_key
{
"id": "22222222-2222-4222-8222-222222222222",
"rulesetVersionId": "11111111-1111-4111-8111-111111111111",
"requestedRulesetVersionId": "11111111-1111-4111-8111-111111111111",
"locale": "de",
"status": "succeeded",
"outcome": "succeeded",
"createdAt": "2026-09-14T12:00:00.000Z",
"terminatedAt": "2026-09-14T12:00:04.000Z",
"failedStepPath": null,
"rawPurgedAt": null,
"payload": { "amount": 249.5, "currency": "EUR" },
"result": { "approved": true, "matchedCreditorId": "acme-gmbh" },
"softFallbacks": null
}

status is lifecycle/storage state (queued, active, succeeded, failed, purged); outcome is the immutable terminal result and stays queryable even after status later becomes purged by retention. Never infer outcome from status alone once a run is old enough to have been purged — always read outcome.

Every poll also reports the run's frozen language and version attribution:

MemberMeaning
localede or en, chosen at submission from the explicit job field or the tenant default. It stays fixed for this run.
rulesetVersionIdThe id of the version actually executed, selected through the ruleset's active version at submission.
requestedRulesetVersionIdThe activated version handle named by the caller; null for historical runs without this attribution.

After an approval or rollback, the requested handle can differ from the version that ran. Use rulesetVersionId to identify the executed rules. Terminal webhook events report the executed id as rulesetId, the requested handle as requestedRulesetId, and the same frozen locale.

Retrieving the result​

For a run whose result fits inline, result on the poll response above already carries it. For a large ("spilled") result, GET /jobs/{id}/result returns the exact canonical bytes rule-line stored — never re-serialized — authenticated the same way as every other call, requiring read:

import { client, getJobResult } from '@one-unity/rule-line-sdk';

client.setConfig({
baseUrl: 'https://api.rule-line.example.com',
auth: 'rl_key_your_api_key',
});

const result = await getJobResult({ path: { id: '22222222-2222-4222-8222-222222222222' } });
if (result.error) {
// Same 410 tombstone `pollJob` returns for an erased run — it carries no
// `code`, so narrow before reading one.
if ('code' in result.error) {
throw new Error(`result fetch failed: ${result.error.code}`);
}
throw new Error(`run ${result.error.id} was erased at ${result.error.erasedAt}`);
}
// The generated type declares `result.data` as `Blob | File`, but the real
// server always sends `Content-Type: application/json` for this route — the
// client's own content-type auto-detection parses it as JSON at runtime, so
// a normal, inline-sized result already arrives as a plain object here.
console.log(result.data);
GET /jobs/22222222-2222-4222-8222-222222222222/result HTTP/1.1
Host: api.rule-line.example.com
Authorization: Bearer rl_key_your_api_key
{ "approved": true, "matchedCreditorId": "acme-gmbh" }

The response also carries a Rule-Line-Result-Sha256 header with the exact digest of the bytes returned, for independent integrity verification. Rule-Line-Result-Locale reports the same frozen de or en language as the poll response. Locale and version metadata add no member to the result body: the declared output bytes and their digest are unchanged.

GET /jobs/{id}/result serves an inline result and a large ("spilled") one identically — your integration never needs to know which storage path a given result took. Either way, the Rule-Line-Result-Sha256 header lets you verify the returned bytes against the digest independently, regardless of size.

Errors​

CodeWhen
VALIDATION_FAILEDThe id path parameter isn't a valid identifier.
NOT_FOUNDNo run with this id is visible to this tenant, or (for the result route) the run hasn't produced a result yet.
AUTHENTICATION_REQUIRED / FORBIDDENMissing, invalid, or under-scoped credentials.

See the full GET /jobs/{id} and GET /jobs/{id}/result references for complete schemas.