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:
| Member | Meaning |
|---|---|
locale | de or en, chosen at submission from the explicit job field or the tenant default. It stays fixed for this run. |
rulesetVersionId | The id of the version actually executed, selected through the ruleset's active version at submission. |
requestedRulesetVersionId | The 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
| Code | When |
|---|---|
VALIDATION_FAILED | The id path parameter isn't a valid identifier. |
NOT_FOUND | No run with this id is visible to this tenant, or (for the result route) the run hasn't produced a result yet. |
AUTHENTICATION_REQUIRED / FORBIDDEN | Missing, invalid, or under-scoped credentials. |
See the full GET /jobs/{id} and
GET /jobs/{id}/result
references for complete schemas.