Check
The one endpoint the product is: a transaction, or an action about to be taken.
POST /v1/check is the product. It takes the facts of one transaction, or of one action you are about to take, checks them against the law's rule set and your company's policies for the same domain — or against your policies alone, where we hold no law for your country — and returns one of four answers. Every check is stored before its answer is sent, and its check_id gives the same answer back for the whole retention period.
The request
{
"rule_set": "de_gifts_entertainment",
"transaction": {
"ref": "INV-2025-0142",
"channel": "card",
"status": "executed",
"actor": { "kind": "agent", "id": "agt_expenses", "on_behalf_of": "emp_4f1c" },
"provenance": { "request_ref": "REQ-88121", "approval_ref": "APR-2025-311" },
"run": "TRIP-4471",
"system": "expenses"
},
"facts": {
"booking_date": "2025-11-20",
"account": "4630",
"amount_gross": { "amount": 2975, "currency": "EUR" },
"recipient": { "name": "Dr. Anna Müller", "org": "Müller GmbH", "customer_number": "10442" }
}
}
| Field | |
|---|---|
rule_set | Which rule set to check against: a law set your account has switched on, or one of your own published policies (see Policies). Either way, every rule set of that domain that covers the transaction and that your account has on answers it — the law for the country it falls in, then your policies — and a transaction counts in all of their totals whichever you named. The version in force on the transaction's date answers it, always. |
rule_version | Optional: the version you expect to answer. It must be the one in force on the transaction's date: another is refused with 409 rule-version-not-in-force, and in_force names the one that is (null where none is, for a transaction dated before every version). |
jurisdiction | Optional: where the transaction falls, as a code of two capital letters — DE, FR, EU — when it may not be the rule set's own. It decides which country's law answers. A transaction outside the rule set's jurisdictions is answered not_covered with no_rule_set_for_jurisdiction, and never counts toward that rule set's totals. Leave it out for a transaction that is the rule set's own; against a policy for several countries, or any, it is required wherever your account has law on for one of them. |
transaction.ref | Your own reference for the transaction, at most 200 characters. Keep it opaque: never a name or an email address. It stays the action's: a reference you send when you report it done is kept beside it, never over it. |
transaction.channel | card, invoice, reimbursement, booking or agent. |
transaction.status | executed for something that happened; proposed for an action not taken yet. Left out, executed — but a key made for an agent says which, and leaving it out is refused (422 invalid-request, required). |
transaction.actor | Who is acting: kind (user, agent or system), id, and for an agent the person it acts for in on_behalf_of. Your rules may turn on it ("an agent refunds up to €30 on its own"). An agent's own key records that agent whatever this says, and names only on_behalf_of here; with your account's own keys, say who acts — an agent your account registered, or a person of your account by their Himmel id (unknown_user otherwise); a check that names nobody records nobody, and a rule that turns on who acts asks for actor. See Authentication. |
transaction.provenance | Opaque references your own systems resolve: the request an agent is working on, the approval it already holds. Each name and each reference is at most 200 characters, a reference a string or null, and neither an email address nor an IBAN; so is actor.id, and actor.on_behalf_of. Longer is refused with 422 invalid-request, code string_too_long, naming the field. |
transaction.run | Optional: the run of an agent this action is part of — the task a person dispatched it to do: a conversation, a ticket, a batch — as your own systems identify it. Opaque like ref: at most 200 characters, never an email address or an IBAN (422 pii-in-facts). Every action of one run names the same, the check log lists them together (run=), and an agent's runs are listed under Your agents. No rule reads it. |
transaction.system | Optional: the system the action is taken in — the shop, the support desk, the ledger — by a short code your company picks: support_desk, finance. Lowercase letters, digits, _ and -, starting with a letter, at most 40 characters, and never none; anything else is refused with 422 invalid-request, code format, before anything is stored. An action has one: named here, or where the check named none by the first request after it that names one — its report, or a check that completes, corrects or asks about it again — it then holds for every booking of the action, and a request naming another is refused with changed. The check log finds actions by it (system=), and an action sent with none is recorded with none, never a system guessed. No rule reads it: it says where the action was taken, as actor says who took it — actor.kind: system is a system acting, nobody named. |
transaction.executed_at | Optional, on an action sent as taken (status: executed): when it was taken, a moment with its offset. It places the action in its day, as a report's executed_at does, and is the action's own moment wherever one is read — an agent's runs, when it last acted. Any moment before the check, on the day the action is dated or a day either side for time zones (not_its_day otherwise), and at most a minute after the check is received, for clocks that differ (after_it_was_sent); refused on an action asked about before it is taken (not_taken), whose report says when it was done. Left out, the moment the check is received — so send it when you report your history: a month of past actions sent in one sitting would otherwise all read as that minute. A completion or a correction keeps its action's moment: leave it out, or send it the same (changed). |
facts | The facts the rule set takes, by name. The catalogue lists them for each version. |
policies | Which of your published policies to layer after the law. Leave it out for all of them; [] for none. A policy named as rule_set always answers. |
explain | true by default: the answer carries a trace of every rule it reached. |
Facts, never conclusions
A fact is something that can be observed: a date, an amount, an account, a code, who received it, whether the receipt names the guests. A rule never asks you for a conclusion — "deductible", "appropriate", "a business trip" — because deciding those is what the rules are for.
This binds an agent most of all. An agent sends what it is about to do and where the instruction came from — the amount, the merchant, the date, the request it is working on — and never its own judgement that a trip is business or that an amount is reasonable. Purpose is a conclusion; provenance is a fact.
Where a rule set takes a person as a fact — who approved the action, approved_by — your own software sends somebody in your account, by their Himmel id, as a signer in your settings is. An id nobody in your account has is refused with 422 invalid-fact, code unknown_user, before anything is stored, and the refusal never repeats it.
A number is zero or more: an amount, each figure beside it, a whole number. One sent below zero is refused with 422 invalid-fact, code below_zero, naming the fact, unless the rule set declares that fact signed, which the catalogue shows. An action taken back is reported, never sent as its negative. A code a fact carries, a string or an account, is at most 200 characters.
Some facts only you can know, and they are checked as you send them, kept with the key that sent them: an approval your software holds, your own figure for an amount in another currency, a customer's number — and so who the customer is. A name, a number or an address never sent before, or an id no answer gave, is a customer with no history yet. The check log shows who sent what.
An agent never names a person
A key made for an agent never sends a person — a fact of type user, such as approved_by — nor the figure your rule set marks as what an approval was given for: Himmel holds them for the agent, and an agent's word is never taken as someone's approval. Sent with a value, one is refused with 422 invalid-fact, code not_the_keys, naming the fact and never its value, before anyone is looked up — so the refusal says nothing of who is in your account — and nothing is stored. Left out, or sent as null, it is read as Himmel holds it and kept so: none, where nothing is recorded on the action. So an answer that needs an approval names who gives it, rather than asking the agent for approved_by; and where one of your people decides a question on the action and it goes on, their decision still stands as their approval (Resolutions). A completion or a correction from the agent's key carries what its action recorded: an approval your software sent with it stands, and a null the agent sends leaves it standing. What an agent reads before it starts names these facts as never sent, and the MCP server's check tool does not offer them.
Your own software, with your account's own keys, and your people signed in send approved_by as before, on their word.
An amount in another currency
Every rule set says the currency its limits are in, and the catalogue lists it as currency. An amount is { "amount": <minor units>, "currency": "<ISO 4217 code>" }. One in another currency than its rule set's is sent with what your own systems record it as in the rule set's currency — your payment processor's settlement amount, your shop's amount in its own currency, the card statement, your ledger — under equivalents, a figure per other currency:
"amount": { "amount": 3500, "currency": "CAD", "equivalents": [ { "amount": 2550, "currency": "USD" } ] }
Himmel picks no rate and converts nothing: the rules read your figure, so "the assistant refunds up to $30" means $30 whatever your customer paid in, and a refund and the order it is for are compared in one currency. An amount sent without the figure its rule set reads is not an error. Where a rule needs it, the answer is undetermined_missing_facts, naming the amount in missing with the currency it is wanted in; an action you took without it still counts toward its customer's counts, such as "two credits in any 90 days", and you send the figure by completing the action. An action that rule sets in two currencies read — your policy in dollars over a law in euros — carries a figure for each. Refused with 422 invalid-fact, naming the fact: a currency ISO 4217 gives no decimals, such as gold (XAU) or XXX, for an amount or any of its figures (code currency); a figure in the amount's own currency, or two in one (equivalents); and a key a money value does not have (type), so a misspelt equivalent is never dropped without a word.
Recipients
Send a recipient as a descriptor — name, org, email, kind (person, the default, or organisation), and the customer or supplier number where you have one (customer_number, supplier_number, vendor_number, creditor_number) — or as an id an answer gave you. A customer you know only by email address can be sent by address alone: the same address is the same customer. A descriptor carries those keys and no other, each value a string of at most 200 characters (an address 254; a number may be a whole number), and never beside an id: a key it does not have is refused with 422 invalid-fact, code unknown_key, naming it - so a misspelt customer_numbr is never dropped without a word - and so is an id beside a name (described_and_named). A descriptor is replaced by a token before anything is stored, and it resolves to one identity per account, so a yearly total follows the recipient rather than the spelling. When two records might be one person, Himmel asks rather than guessing: see Recipients.
The answer
Every answer carries the same core:
| Field | |
|---|---|
check_id | The receipt. Store it with what you did. |
state | One of determined, undetermined_missing_facts, not_covered, contested. |
effective_date, as_of | The date the rules were applied as of, and the moment the history was read. |
layers.law | The law's answer: outcome, basis, and consequences such as the amount that is not deductible. |
layers.policies | Each of your policies' answers, in the order they ran. |
recipient | The identity the recipient resolved to, and — where the rule set totals money per recipient per fiscal year — fy_total: the recipient's other bookings of the year that it counts, as known when it answered, this one not included. history_used names every total the answer read. |
history_used | Each total the answer depended on, and the stored transactions behind it. |
rechecks | The earlier answers this booking changed, each issued again before this answer was given: new_check_id, the check it supersedes, that booking's booking_date and its outcome now. Left out when it changed none. |
citations | The paragraphs and clauses the answer applied. |
confirmations | Which rules were reached, and who at your company confirmed each one, and when. |
not_checked | What this rule set deliberately does not apply, and why. It is a list inside the answer, never a state. |
trace | Every rule reached, in order, and which branch applied. |
And then what its state adds:
{ "state": "undetermined_missing_facts",
"missing": [ { "fact": "recipient", "description": "Who received the gift", "needed_by": "de_gifts_entertainment.gifts.limit" } ] }
{ "state": "not_covered",
"reason": "awaiting_confirmation",
"unconfirmed_rules": ["de_gifts_entertainment.gifts.limit"] }
reason is one of awaiting_confirmation, rule_flagged, policy_gap, no_rule_matched, no_rule_set_for_jurisdiction or rule_set_broken.
rule_set_broken is an action that happened whose rules could not run on its facts - a rule that divides by a figure that is zero, say. Whatever its rules can say, an action you report as taken happened: it is kept, counts in every total, and your account is told (check.rule_set_broken, Alerts); detail names each rule set by its version and what the rules said, never a value you sent; and it is checked again once a version that can answer it is confirmed. An action you ask about before taking it is answered 500 rule-set-broken instead, and nothing is kept: nothing has happened.
Two checks of one account sent at the same moment are answered one after the other: the second waits for the first and reads what it stored - so two credits sent at once against "two in any 90 days", after one, are answered allowed and not_allowed, never both allowed. Other accounts never wait on yours.
A total counts every action it covers, whether or not it can read what the action came to: "two credits in any 90 days" counts a credit you gave without its amount. What such a total adds up to is not known until that amount is, so an answer that needs it names the fact with of, the action it is missing from — its transaction_id and effective_date — as in { "fact": "amount", "currency": "EUR", "of": { "transaction_id": "txn_01J8...", "effective_date": "2026-01-05" } }. A missing amount says the currency it is wanted in: its rule set's. Send it by completing that action, not in this check: the answers it reached are then issued again.
A fact you send as null is recorded as none, which is not the same as leaving it out — until a rule needs its value. Where a rule has to order it, add it, or take it as true or false, the answer names it in missing as it names a fact left out, and a fact the rule set requires is missing whenever it is null. Where your rule set says what none means — approved_by: null for a refund nobody approved — the rules read it as it is. The check keeps the facts as you sent them, the null included.
{ "state": "contested",
"question": { "id": "de_gifts_entertainment.q.appropriate",
"candidates": ["appropriate", "not_appropriate"],
"reason": "Appropriateness is judged case by case, and this is above your per-person sign-off amount",
"wording": "The law deducts only what is appropriate by general standards..." },
"signer": { "user": "usr_01J9Q4TZ8M..." } }
A contested answer carries the rule's wording exactly as your rule owner confirmed it. signer is the Himmel id of the person your settings name for the question, never their name. Himmel never picks between the candidates: the named person does, and records it as a resolution. Where the question's rule says what each answer leads to, that decision answers the action again: the rules after the question answer too, and the new answer lists the decision it read in decisions.
Answers are as of a date
Some rules depend on earlier transactions: the limit on gifts is per recipient per fiscal year, across cards, invoices and reimbursements. Every answer says the date it is valid as of and the history it used. When a later booking changes an earlier one's treatment — a €30 gift in March is within the limit until a €25 gift to the same client in November takes the year over it — Himmel issues a new answer for the March booking before it answers November, with supersedes_check_id pointing at the old one, and raises a check.rechecked alert. November's answer lists it under rechecks. The old answer is never edited. A booking dated before answers already given works the same way, and the alert's reason says late_booking rather than later_transaction.
Proposals
An action checked before it is taken is status: "proposed". It is answered exactly as if it had happened, but it moves nobody's total, so asking does not change the answer to the next question.
It is dated the day it is asked: its date - the one the rule set reads as the action's own, action_date in the sandbox's playbook - is today, in UTC, a day either side for time zones. An action reported after it happened may be dated earlier, and none is dated after the day it is sent. Otherwise the check is refused with 422 invalid-fact, naming the date and the days it may have (not_the_day_asked, after_the_day_sent). An action you asked about keeps the day you asked when you complete or correct it later.
When the action has happened, or will not happen, report it:
curl https://api.gethimmel.com/v1/checks/chk_01J8.../execution \
-H "Authorization: Bearer $HIMMEL_KEY" -H "Content-Type: application/json" \
-d '{ "executed": true, "ref": "CN-2025-0311", "system": "expenses" }'
executed_at says when it was done, a moment with its offset that places the action in its day: between when you asked and this report, a minute either side for clocks that differ, else 422 invalid-request (before_it_was_asked, after_it_was_reported). Left out, it is the moment of the report.
On executed: true the transaction joins history at that moment, and any earlier answer whose totals it changes is issued again, with an alert. On executed: false — with the reason abandoned, refused_by_caller or failed — it is closed and never counts. ref is your own reference for the action taken — a credit note's number beside the ticket's — opaque like transaction.ref: one that looks like an email address or an IBAN is refused with pii-in-facts. It is kept beside the action's own reference, never over it: GET /v1/checks/{id} shows it as execution.ref, each row of the log as transaction.executed_ref, and the log finds the action by either (ref=); a report of an action not taken keeps none, and a repeat of a report answers with the first one's. system is where the action was taken: sent the same as the check's, or left out — another is refused with changed — and where the check named none, the report names it for the action. A repeat of a report names no system the action does not have. A proposal nobody reports on within 24 hours is closed as expired, and a report after that is refused with not-a-proposal. A proposal never becomes history without being reported. A key made for an agent reports on its agent's own proposals: one on anybody else's is refused with 403 not-the-agents-action.
The action itself is checked again when you report it done, as it stands then. The answer you were given is the answer of the moment you asked; if, while the action was open, another booking joined its totals — a colleague's credit to the same customer, say — or your rule owner confirmed a new version or corrected a setting, the report answers the action again, and where that comes out otherwise the new answer replaces the one you were given (supersedes_check_id, recheck_reason: proposal_executed), with a check.rechecked alert where its state or outcome changed. The report's response names it in superseded_by, which is empty where your answer stands as it was given; the answer you were given stays on record, unchanged. A change your rule owner has not confirmed yet never takes an answer away: your answer stands until they confirm it, and their confirmation checks it again.
Each report that changes the transaction is kept with who sent it — your key, or the person signed in to the console — and GET /v1/checks/{id} shows it under execution, beside the answer.
When an answer names a person
Where your rules give an action to a person — an approval above a limit, a question only a person answers — the answer names them by their Himmel id, and when an agent asked with a key made for it, Himmel asks them. The sandbox's sample playbook gives its team lead retention discounts above 15 %. Its assistant, asking with its own key before it offers a customer 20 %:
curl https://api.gethimmel.com/v1/check \
-H "Authorization: Bearer $ASSISTANT_KEY" -H "Content-Type: application/json" \
-d '{ "rule_set": "policy.sandbox.customer_support",
"transaction": { "status": "proposed", "ref": "CASE-5001" },
"facts": { "action_date": "'"$(date -u +%F)"'", "action": "retention_discount",
"customer": { "customer_number": "C-1003" }, "discount_percent": 20 } }'
is answered determined, the playbook's layer needs_approval with the team lead as its approver, and beside the answer requests, whom Himmel asked:
"requests": [ { "id": "apr_01J9...", "asked_of": "usr_team_lead", "asks": "approval", "status": "waiting",
"asked_at": "2026-10-12T16:08:26Z", "waits_until": "2026-10-19T16:08:26Z", "...": "..." } ]
Himmel emails the person asked within a minute, at the address they sign in with, linking a page of the console where they approve or refuse, signed in (Resolutions). The request waits a week at most, until waits_until, and the proposal waits with it, so your agent can tell whoever it acts for when it will have been decided by. Meanwhile:
- It never asks the person itself. Only their answer in Himmel counts: an approval the agent got some other way is never read, and a check that names who approved is refused (An agent never names a person).
- It hears what they decided by asking again, naming the action (Asking again) and sending only the facts an answer asked for. Once the team lead approved, the assistant asking again is answered
allowed, their approval listed in the answer'sdecisions. Refused, the answer staysneeds_approval,requestssaysrefused, and nobody is asked about the action again. Asked before they decide, the answer is as it was and the request still waits. Through the API,GET /v1/approvals/{id}says where a request stands without asking again; through the MCP server the agent asks again with thecheck_idof an answer to the action. - It reports what became of the action, as for any proposal.
An action handed to a person. Where your rules hand an action to a person and name them - a rule's to, as the template's clause 2.7 names the team lead - the playbook's layer says whom, handed_to, beside approver. When an agent asked with its own key, Himmel tells that person once, by email and on Home (check.handed_over, Alerts): a notice, never a request - nothing waits on them in Himmel, nobody approves anything for it, and they act in your own systems. The agent asking again about the same action tells them nothing new.
A contested answer's question is asked the same way, of its signer (asks: "question"). Himmel asks only when an agent asked with its own key: your own software and your people keep their approvals in their own systems and send them as approved_by — the same 20 % discount sent with the sandbox's own key, naming the assistant in transaction.actor, is undetermined_missing_facts, asking for approved_by, and nobody is asked. And an approver is asked only where your rule set marks the fact that records who approved (approved_by, approval: true), so that their approval can be read: an answer that names a person with no requests asked nobody.
Trying it in the sandbox. The sandbox's team lead and head of support, usr_team_lead and usr_head_of_support, are people of the sandbox whom nobody signs in as, so a request made of either waits its week and lapses. To see an approval through, an admin of the sandbox, its playbook confirmed, names themselves the team lead from today — in the console's settings, under "Say who decides", or with PUT /v1/tenant-facts/support_team_lead, its value their own Himmel id (Your settings) — before the assistant asks. The assistant's discounts asked from then name them: they are emailed, approve on the request's page, and the assistant asking again is answered allowed. An action asked about before keeps the day it was first asked, and the team lead of that day. A key of the assistant's own is made as any agent's is (A key of its own for every agent).
Asking again
An action you are still asking about is asked about again by naming it: send the check with status: "proposed" and the action's booking in transaction.completes, by the transaction_id its latest answer carries:
curl https://api.gethimmel.com/v1/check \
-H "Authorization: Bearer $HIMMEL_KEY" -H "Content-Type: application/json" \
-d '{ "rule_set": "policy.acme.customer_support",
"transaction": { "status": "proposed", "completes": "txn_01J8..." },
"facts": { "delivered_on": "2026-10-07" } }'
It is the same action, asked again. Its facts are the booking's with the ones you send added — the fact an answer asked for, say — and a fact it recorded is left out or sent the same: a larger refund is a new question, which an approval of the smaller one does not cover, and is refused with changed. So send only the facts an answer asked for, or none: the action keeps the rest, and the day you first asked. Its day read again as today, a day later, is refused with changed too, the refusal naming the fact to leave out. Its answer replaces the one that stood, so the check log lists the action once, and it reads what was decided on the action since you last asked: where the person an answer named approved, or decided its question, while you waited, you are answered with their decision. Nobody it names is asked again — a request still waiting is the one it was, and requests says where it stands — and a person who decided is not asked again. It keeps the day you first asked, and the 24 hours before a proposal nobody reports on closes run from your latest asking. Report what became of the action on any of its answers: the check_id you kept from your first asking reports the action as it stands.
Asking again is for an action still being asked about, named by the booking that stands for it, in the run it was asked in: one taken is refused with not-a-proposal — send the facts it lacked by completing it — one closed without being taken with action-not-taken, one taken back with action-taken-back, and a booking already asked again with answer-replaced, naming the booking that stands (transaction_id) and its answer (standing). What the action was is held as for a completion: anything said otherwise is refused with changed. A question asked again without naming the action is a new action: answered on the books as they stand, with a request of its own, the person it names asked again.
Taking an action back
An action taken can be taken back afterwards — a credit given by mistake and clawed back, a refund whose payment failed. Report it on any answer the action has, the check id you kept or a later one:
curl https://api.gethimmel.com/v1/checks/chk_01J8.../reversal \
-H "Authorization: Bearer $HIMMEL_KEY" -H "Content-Type: application/json" \
-d '{ "reversed_at": "2026-03-11T09:00:00Z", "ref": "CB-2026-0311" }'
Both fields are optional: reversed_at is when it was taken back, else the moment you report it; ref is your own reference for taking it back, opaque like transaction.ref. From then the action counts toward nothing: every total it was in is worked out again without it, as if it had never been taken, and each earlier answer that moves is issued again (recheck_reason: reversal), with a check.rechecked alert where its state or outcome changed — those of actions that came while it stood included. So a customer's third credit of a day, refused while a mistaken first one stood, is answered again as the second once the mistake is taken back. Its own answers stay as they were given, and every earlier answer is still given back from its record.
Take an action back this way, never by booking its negative: a credit of -€15 is an action of its own, which a count of credits counts. The log lists an action taken back with transaction.status reversed and reversed_at; GET /v1/checks/{id} shows reversal on every answer to it — when, your reference, and who reported it — and it waits on nobody: a decision on it is refused with action-taken-back, and so is completing it. An action still being asked about is reported taken or not instead, and one never taken has nothing to take back (action-not-taken). Reporting it twice the same way is answered the same way; a report that says another moment or reference is refused with conflict. A key made for an agent takes back its agent's own actions; anybody else's is refused with 403 not-the-agents-action, and is for your account's own keys or your people to take back.
Completing an action
An action taken without a fact its answer needs — a credit given before its amount was known — waits on that fact. When you have it, send it as a check that names the action in transaction.completes, by the transaction_id its answers carry:
curl https://api.gethimmel.com/v1/check \
-H "Authorization: Bearer $HIMMEL_KEY" -H "Content-Type: application/json" \
-d '{ "rule_set": "policy.acme.customer_support",
"transaction": { "completes": "txn_01J8..." },
"facts": { "amount": { "amount": 1000, "currency": "EUR" } } }'
The check's facts are the booking's with the ones you send added, and the complete booking stands in for the first in every total: the first leaves the totals it was in, the complete one joins, and the answers either reaches are issued again. Both stay on record. The answer replaces the one that stood for the action — its supersedes_check_id names it, and the old answer's superseded_by names the new one — so the check log lists the action once, with the complete answer, and the review queue stops waiting on the fact. A decision your people made on the old answer carries over where the new one asks the very same question.
It is the same action. Its ref, run, system, channel, actor, provenance, jurisdiction and policies, and when it happened, are the booking's: leave them out, or send them as they were. A run is never given where the booking had none: every booking of an action sits in its one run. A system may be, where the action had none, and every booking of the action then carries it. The reference your report sent for the action taken is carried too. A key made for an agent completes the actions its agent took, and anybody else's is refused with 403 not-the-agents-action. And a completion adds facts; it never changes one: a fact the booking recorded a value for is left out or sent the same — a recipient as the same customer — while one left out, or sent as null, may be given. An amount it recorded may be sent again with the figures it lacked in other currencies (equivalents), which are added to it; a figure it recorded is sent the same or left out. Anything said otherwise is refused with invalid-request and the code changed, naming the field and never its value. An action still being asked about is asked about again instead, the check sent as proposed, and one already completed is completed by the check that completed it: answer-replaced names that booking and its answer.
Correcting an action
An action whose facts were sent wrong — €25 where €15 was given, another customer's number, the wrong day — is corrected, never sent a second time: a second action would count beside the first. Send a check that names it in transaction.corrects, by its transaction_id, with the facts as they should have been:
curl https://api.gethimmel.com/v1/check \
-H "Authorization: Bearer $HIMMEL_KEY" -H "Content-Type: application/json" \
-d '{ "rule_set": "policy.acme.customer_support",
"transaction": { "corrects": "txn_01J8..." },
"facts": { "amount": { "amount": 1500, "currency": "EUR" } } }'
The check's facts are the booking's with the ones you send in their place — one sent as null recorded as none, as a check records it, and an amount with the figures you send beside it — and provenance likewise. The corrected booking stands in for the first in every total, as a completion's does: the first leaves the totals it was in, the corrected one joins its own, and each earlier answer either moves is issued again with recheck_reason: correction and an alert where its state or outcome changed. Its answer replaces the one that stood, so the check log lists the action once, with transaction.corrects on its row, and the answer to it as first sent stays on record, one click away. Part of an amount taken back is the amount corrected.
It is the same action: its ref, run, system, channel, actor, jurisdiction, policies and when it happened are the booking's, and anything said otherwise is refused with changed — a run given where the booking had none included; a system may be given where the action had none, for every booking of it. An action that was another thing than you sent — checked against the wrong rule set, done by somebody else — is taken back and sent again. A decision your people made on the answer to the facts as first sent is asked again, since the facts it was made on changed; it stays on the answer it was made on. A correction that changes nothing is refused with unchanged, one sent with completes too with both; an action still being asked about, one taken back, one completed or corrected already, and from a key made for an agent one its agent did not take, are refused as a completion of them is.
Retries
Send an Idempotency-Key header with a check, and a retry with the same key and the same body gets the same check_id back — never a second check, and never a second proposal. The same key with a different body within 24 hours is refused with idempotency-conflict. Keys are per environment: the same key sent with a test key and with a live key is two requests. Every other POST that changes something takes a key the same way: see Getting started.
Reading answers back
GET /v1/checks/{id} returns the answer as it was stored, with superseded_by when a newer answer has replaced it, and resolutions, every sign-off and override your people recorded on it in the order recorded, with resolution the latest — one carried over from the answer before, the question unchanged, names the answer it was given on in given_on. Beside it, never inside it, come three more. facts are the facts the booking was sent with, as stored: a recipient by the id it resolved to when it was sent, never a name, and everything else as you sent it. They are the booking's, so every answer to it shows the same; the answer's own recipient says which identity that answer used. sent_by says who sent the check: {"kind": "api_key", "id": "key_...", "prefix": "hk_live_7f3a", "name": "support tool"} for a key — its prefix and the name it was given, never the key — or {"kind": "person", "id": "usr_..."} for someone signed in to the console. That is not always who acted: your software names who acts in transaction.actor, and someone trying an agent's example in the console sends it as themselves. An answer Himmel issued again was sent by nobody, so its sent_by is null and recheck_reason says why. An answer that completed an action was sent by whoever sent the completion, though it replaced the answer before it. For an action asked about before it was taken, execution says what became of it: its status now (proposed until you report, then executed, abandoned or expired), executed_at or the reason it was not taken, ref, the reference your report sent for the action taken, system, the system it is taken in as its check or its report named it, and who reported it and when. The answer itself still says proposed, because that is what was answered. GET /v1/checks is the check log, newest first, fifty to a page. It holds every answer ever given, those a later answer replaced included; current=true keeps only the answers that stand, one per booking. state and outcome take several values, separated by commas. Where our law answers, outcome is the law's; policy_outcome=not_allowed (or several, separated by commas) lists what your policy answered, whatever the answer as a whole says. missing=recipient keeps the answers that ask for that fact, consequence=non_deductible_amount those that carry that consequence, rechecked=true those that replaced an earlier answer, and rerun= those one re-run issued again when the rules changed (see Confirmations). It also filters by rule set, actor and transaction status.
To find an action the way your own systems know it:
| Parameter | |
|---|---|
ref | The bookings you sent with this transaction.ref, or reported taken with it (execution.ref), exactly as you sent it. |
transaction | One booking's answers, by its transaction_id: each booking listed under an answer's history_used opens this way. With current=true, the one that stands. A booking a later check completed is one action with the booking that completed it, so either id lists the answers to both. |
effective_from, effective_to | The actions that happened between these two days, both included, as each answer is dated (effective_date): the day its facts give, or where they give none the day it was answered. Dates only, YYYY-MM-DD. A booking of last year sent today is found under last year. |
from, to | When the answer was given instead: from one moment, included, to another, not included. A date alone is its midnight, UTC. |
recipient | One customer's actions, by the id of any of its records: every record you answered to be the same person, or that a confirmed matching rule linked, counts as one, as their yearly total does. A record an open question only proposes does not, nor one split off again. |
actor | What one person or agent did, by the id the check named — or any of several, separated by commas: actor=agt_support,agt_refunds, the agents one person answers for. |
on_behalf_of | The actions done for this person, by the id their checks named as actor.on_behalf_of, exactly: your own systems' id for them. |
run | The actions of one run of an agent, by the transaction.run their checks named, exactly as sent. With actor, one agent's run — what each run of an agent links to. |
system | The actions taken in this system, by the code their check or report named; none for those whose system nobody named. Several, separated by commas: system=finance,none. A value that is no system's code is refused with 422 invalid-request, code format. |
actor_kind | What one kind of actor did, as the check named it in transaction.actor.kind: agent, user or system, or several separated by commas. actor_kind=agent is what every agent did, which the map of your agents counts. Any other value is refused with 422 invalid-request, code enum. |
answer | The answers of one kind, or of several separated by commas, each read as the map counts it and the console marks it: by your policy's answer where one answered - where our law answers too, the answer's own outcome is the law's - and otherwise by the answer itself. allowed: determined, and the action may stand; approval: it needs the approval of the person your rules name; person: it goes to a person, or a named person decides it; refused: not allowed; other: an outcome your policy declares in its own word; open: no answer your rules settled - not covered, or a fact missing. Every answer is of exactly one kind. Any other value is refused with 422 invalid-request, code enum. |
against_rules=true | The actions taken against your rules: reported taken, with an answer one of whose policies is determined with an outcome that does not let the action stand - any but allowed and not_applicable. Exactly the actions check.against_rules is raised for; with current=true, by the answer that stands. |
To find what waits on somebody at your company:
| Parameter | |
|---|---|
waiting=true | The answers that stand and wait on a person: a question nobody has decided (a decision carried over from the answer before counts), an answer your rules do not give yet, and facts missing from an action that was taken. An action still being asked about waits only on a "same customer?" question — the facts it lacks are its asker's to send — and an action never taken, because it expired or you reported it not done, waits on nobody. GET /v1/review counts these in groups. |
reason | The not_covered answers not covered for this reason: policy_gap (your policy does not say), awaiting_confirmation, rule_flagged, no_rule_matched, no_rule_set_for_jurisdiction or rule_set_broken. Several, separated by commas. |
question | The contested answers that ask this question, by its id. |
request | The actions Himmel asked a person about, by where each request stands now: waiting, approved, refused, decided or lapsed — one, or several separated by commas. With waiting=true, request=waiting is the review queue's approvals. |
asked_of | The actions Himmel asked this person about, by their Himmel id. With request, a request to them that stands so: asked_of=usr_…&request=waiting is what waits on them. |
A parameter the log does not take is refused with 422 invalid-request, naming it in errors[].field (code unknown_parameter), rather than ignored, as on every endpoint: a misspelt filter never reads as no filter at all.
The log lists the newest answer first. With order=effective_date it lists by the day each action happened instead, newest first — within a day, the action asked about or booked last comes first, and one booking's answers newest first — so an answer issued again never moves its row, and last year's bookings sent today sit under last year. With current=true that is one row per action. before continues either order from the last page's next_before.
Each row of the log carries what a list needs without opening every answer: sent_by, as above; transaction.amount, what the booking was for — the fact its rule set names as its amount, for our German gifts rule set what it was booked at, tax included, with the figures you sent beside it (equivalents), and nothing where the rule set names none; transaction.measures, what the action came to in another measure than money - each whole number a figure your rule set marks as approved covers, by its fact, such as a retention discount's {"fact": "discount_percent", "value": 20} where your policy marks approved_percent: { type: int, approval: discount_percent } - and [] where it marks none; layers, what each rule set made of it, law first — its kind, rule_set, state and outcome, so a list of what your policy found shows the policy's verdict where the answer's outcome is the law's, the rule that gave it that answer or asked its question, and the approver its answer names where it needs_approval; for an answer that is not settled, what it waits on — missing (the facts it asks for), reason (why it is not covered) or question (what a named person decides), with signer, who decides it; superseded_by, the newer answer that replaced it; and resolution, the latest sign-off or override recorded on it.
A row's transaction also says what belongs to the action rather than to the answer, since an answer is issued again whenever what it depends on moves: sent_by, who sent the action — the key or the person that sent its first answer — where the row's own sent_by is the answer's and empty on an answer issued again; resolution, the latest sign-off or override recorded on any answer to the booking, with the check_id of the answer it was recorded on — an earlier one when the answer has since been issued again asking another question, for a decision carries over only where the very same question is asked again (see Resolutions); run, the run of an agent it was part of, where its check named one; system, the system it was taken in, where one was named; ref, the action's own reference, and executed_ref, the one your report sent for the action taken; and facts, the facts it was sent with, a recipient by its id.
Endpoints
/v1/checkScope checkCheck a transaction, or an action about to be taken
Returns exactly one of four states - determined, undetermined_missing_facts, not_covered,
contested - with HTTP 200 for all four. Missing facts, an open recipient match and an
unconfirmed rule are answers, never errors.
transaction.status says what the check is: proposed for an action about to be taken, executed
for one taken. A key made for an agent says it, and leaving it out is refused (422 invalid-request,
required); from your account's own keys, and signed in, it is executed when left out.
The check_id is the receipt: store it against whatever you then do, and GET /v1/checks/{id}
reproduces this answer for the full retention window. The answer is stored before it is sent, so
the check is there the moment you read it; if the connection drops before you do, send the same
request again with the same Idempotency-Key and the stored answer comes back.
For an action a key made for an agent asks about, each person the answer names - a policy's
approver, a question's signer - is asked by Himmel, and requests beside the answer says so: whom,
when, and where each request stands (GET /v1/approvals/{id}). Ask about the same action again by
naming it in transaction.completes with status: proposed: its answer reads what was decided on
it, and nobody it names is asked again.
Himmel answers; the caller enforces. No endpoint blocks an action, and none takes one.
Parameters
Idempotency-Keyheader · string or nullMakes a retry safe: the same key with the same request within 24 hours gets the first answer again, and nothing is done twice. The same key with a different request is
409 idempotency-conflict. Keys are per environment and per path.
Request body · application/json
rule_setstringrequiredWhat to check against: a law set your account has switched on, or one of your own published policies. Either way every rule set of that domain that covers the transaction and that your account has on answers it: the law for the country it falls in, where your account has that law switched on, then your policies. With no law set among them, your policies are the whole answer, and it states no legal consequence.
rule_versionstring or nullThe version you expect to answer. The version in force on the transaction's date answers, always: named, it must be that one, and another is refused (
rule-version-not-in-force), naming the one in force.jurisdictionstring or nullWhere the transaction falls, as a code of two capital letters (
DE,FR,EU), when it may not be the rule set's own. It decides which country's law answers. A transaction outside the rule set's jurisdictions is answerednot_covered, and never counts toward that rule set's totals. Against a policy for several countries, or any, it is required wherever your account has law switched on for one of them.transactionTransactionIn10 fields
refstring or nullThe caller's opaque reference, at most 200 characters. Never PII.
channel"card" | "invoice" | "reimbursement" | "booking" | "agent"status"executed" | "proposed"executedis something that happened.proposedis an action the caller has not taken yet: checked identically, but out of history until the caller reports the outcome. Left out, it isexecuted- but a key made for an agent says which, and leaving it out is refused (required): nothing an agent sends is recorded as done because a field was left out.actorActorIn or null3 fields
kind"user" | "agent" | "system"idstring or nullThe agent's own identifier - or, for a person (
kind: user), their Himmel id: from your account's own keys a person of your account (unknown_user,left_the_account).on_behalf_ofstring or nullThe person the agent acts for, as your own systems identify them: an opaque id, never a name or an email address. They need no account here.
provenancemap of string or nullOpaque references the caller resolves in its own systems - a request reference, an approval it already holds - each name and reference at most 200 characters. Facts about where the instruction came from, never the agent's conclusion about what it means.
runstring or nullThe run of an agent this action is part of - the task a person dispatched it to do: a conversation, a ticket, a batch - as your own systems identify it: an opaque reference of at most 200 characters, never an email address or an IBAN. Every action of one run names the same, and the check log lists them together (
run=). A completion or a correction carries the action's.systemstring or nullThe system the action is taken in - the shop, the support desk, the ledger - by a short code your company picks:
support_desk,finance. Lowercase letters, digits,_and-, starting with a letter, at most 40 characters, and nevernone; anything else is refused (format). An action has one: named on its check, or where that named none by the first request after it that names one, it holds for every booking of the action, and another is refused (changed). No rule reads it: the check log finds by it (system=).executed_atstring or nullWhen an action sent as taken (
status: executed) was taken, a moment with its offset: it places the action in its day and gives it its own moment - an agent's runs, when it last acted. Any moment before the check, on the day the action is dated or a day either side, for time zones (not_its_dayotherwise), and at most a minute after the check is received, for clocks that differ (after_it_was_sent). Left out, the moment the check is received. Refused on an action asked about before it is taken (not_taken); a completion or a correction keeps its action's, so it is left out or sent the same (changed).completesstring or nullAn action already taken whose missing facts this check sends, by the
transaction_idits answers carry. The facts are that booking's with the ones sent added, and the complete booking stands in for it in every total; its answer replaces the one that stood. It is the same action:ref,channel,actor,provenance,jurisdiction,policiesand when it happened are the booking's, and may be left out. A fact the booking recorded a value for is left out or sent the same (changedotherwise); one left out or recorded as null may be given. Sent withstatus: proposed, an action still being asked about, asked about again: the booking that stands for it, its answer reading what was decided on the action.correctsstring or nullAn action already taken whose facts were sent wrong, by the
transaction_idits answers carry: this check's facts are that booking's with the ones sent in their place, and the corrected booking stands in for it in every total; its answer replaces the one that stood, and a decision made on that one is asked again. It is the same action -ref,channel,actor,jurisdiction,policiesand when it happened are the booking's (changedotherwise: an action that was another thing is taken back and sent again). One or the other ofcompletesandcorrects.
factsobjectThe facts the rule set takes, by name, as the catalogue lists them. An amount is
{"amount": <minor units>, "currency": "<ISO 4217>"}; one in another currency than the rule set's carriesequivalents, what your own systems record it as in the rule set's currency, which is what the rules read - without it, the answer asks for it. A key made for an agent never sends a person, nor the figure an approval was given for: Himmel holds them for the agent, and one sent with a value is refused (not_the_keys).policieslist of string or nullWhich of your published policies for the domain answer beside the law: left out, all of them;
[], none. A policy named asrule_setalways answers.explainboolean
Examples
determined: With every rule confirmed, the same booking is answered.
{
"rule_set": "de_gifts_entertainment",
"transaction": {
"channel": "agent",
"status": "executed"
},
"facts": {
"booking_date": "2023-03-14",
"account": "4630",
"recipient": "r1",
"amount_net": {
"amount": 2000,
"currency": "EUR"
},
"amount_gross": {
"amount": 2380,
"currency": "EUR"
}
}
}undetermined_missing_facts: Where nothing is recorded about the occasion, the answer is that we cannot tell — not that it is missing.
{
"rule_set": "de_gifts_entertainment",
"transaction": {
"channel": "agent",
"status": "executed"
},
"facts": {
"booking_date": "2023-03-14",
"account": "4650",
"participants": [
"r1",
"r2"
],
"participants_recorded": true,
"restaurant": true,
"invoice_attached": true,
"amount_net": {
"amount": 9500,
"currency": "EUR"
},
"amount_gross": {
"amount": 11305,
"currency": "EUR"
}
}
}not_covered: Until your rule owner confirms the rules, a check comes back saying so rather than answering.
{
"rule_set": "de_gifts_entertainment",
"transaction": {
"channel": "agent",
"status": "executed"
},
"facts": {
"booking_date": "2023-03-14",
"account": "4630",
"recipient": "r1",
"amount_net": {
"amount": 2000,
"currency": "EUR"
},
"amount_gross": {
"amount": 2380,
"currency": "EUR"
}
}
}contested: €250 a head is above the €100 you set for sign-off, so a named person confirms it was appropriate.
{
"rule_set": "de_gifts_entertainment",
"transaction": {
"channel": "agent",
"status": "executed"
},
"facts": {
"booking_date": "2023-03-14",
"account": "4650",
"participants": [
"r1",
"r2"
],
"participants_recorded": true,
"occasion_recorded": true,
"restaurant": true,
"invoice_attached": true,
"amount_net": {
"amount": 50000,
"currency": "EUR"
},
"amount_gross": {
"amount": 59500,
"currency": "EUR"
}
}
}Responses
200 One of the four states, always with HTTP 200.
One of 4, told apart by state.
effective_datestringrequiredThe date the rules were applied as of.
as_ofstringrequiredThe moment the history was read.
layersLayersrequired2 fields
lawLayerAnswer or nullThe law's answer. Left out where no law set is part of the answer - your policy's alone, for a country we hold no law for or one your account has not switched on.
12 fields
rule_setstringrequiredrule_versionstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredoutcomestring or nullbasisstring or nulllaw,policyorgoodwill.consequenceslist of Consequence or null5 fields
keystringrequiredvalueanyrequiredMoney as minor units and currency, or a number, a flag or a date.
rulestringrequiredbasisstring or nulllaw,policyorgoodwill.citationCitation or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
approveranyWho a policy says must approve, where it names one.
handed_toanyWhom a policy hands the action to, where its rule names one: a person in your account, by Himmel id. Where an agent asked about the action, Himmel tells them (
check.handed_over).tenant_facts_usedlist of string or nullYour settings this answer read.
reference_versionsmap of integer | string or nullWhich version of each reference table it read, such as dated VAT rates.
waiting_onstring or nullA policy rule that reads the law's answer waits for the law to settle:
law.
policieslist of LayerAnswer or nullYour policies for this domain, in the order they ran.
12 fields
rule_setstringrequiredrule_versionstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredoutcomestring or nullbasisstring or nulllaw,policyorgoodwill.consequenceslist of Consequence or null5 fields
keystringrequiredvalueanyrequiredMoney as minor units and currency, or a number, a flag or a date.
rulestringrequiredbasisstring or nulllaw,policyorgoodwill.citationCitation or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
approveranyWho a policy says must approve, where it names one.
handed_toanyWhom a policy hands the action to, where its rule names one: a person in your account, by Himmel id. Where an agent asked about the action, Himmel tells them (
check.handed_over).tenant_facts_usedlist of string or nullYour settings this answer read.
reference_versionsmap of integer | string or nullWhich version of each reference table it read, such as dated VAT rates.
waiting_onstring or nullA policy rule that reads the law's answer waits for the law to settle:
law.
engine_versionstringrequiredcheck_idstringrequiredThe receipt.
GET /v1/checks/{id}gives this answer back.transactionTransactionSummaryrequired9 fields
transaction_idstringrequiredstatus"executed" | "proposed"requiredproposedstays out of every total until you report that it executed.channel"card" | "invoice" | "reimbursement" | "booking" | "agent"requiredrefstring or nullYour own reference, as you sent it.
runstring or nullThe run of an agent the action is part of, as your systems identify it.
systemstring or nullThe system the action is taken in, by your company's code for it.
actorActor or null3 fields
kind"user" | "agent" | "system"requiredidstring or nullon_behalf_ofstring or nullThe person an agent acted for.
completesstring or nullThe booking whose missing facts this check sent, where it completed one: the same action, which this booking stands in for in every total.
correctsstring or nullThe booking whose facts sent wrong this check corrected, where it corrected one: the same action, as it should have been, which this booking stands in for in every total.
created_atstringrequiredrecipientRecipientSummary or null4 fields
resolutionstringrequiredresolved,unresolved,probable_matchormissing.recipient_idstring or nullreview_itemstring or nullThe open probable match, where there is one.
fy_totalMoney or nullThis recipient's total for the fiscal year, where the answer's first rule set totals money per recipient per fiscal year: the recipient's other bookings of the year that it counts, as known when the answer was given - this one not included, and later ones too where it counts the whole year. Left out where no such total was read;
history_usednames every total an answer read.2 fields
amountintegerrequiredcurrencystringrequired
history_usedlist of HistoryInput or null5 fields
rule_setstringrequiredinputstringrequiredThe rule set's name for this total, such as
recipient_fy_gifts.countintegerrequiredtransactionslist of HistoryTransactionrequired2 fields
transaction_idstringrequiredeffective_datestring or null
totalMoney | integer | string or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
citationslist of Citation or null4 fields
sourcestringrequiredThe source's key within the rule set, such as
estg.paragraphstring or nullclausestring or nulltext_refstring or nullThe passage in the rule set's own copy of the source,
sources/<file>#<anchor>.
confirmationslist of ConfirmationUsed or null3 fields
rulestringrequiredconfirmed_bystring or nullconfirmed_atstring or null
decisionslist of DecisionRead or nullEach question on this action a person decided, read as its answer: where the question's rule says what each answer leads to, a sign-off or an admin's override issues the action's answer again, and every answer to it after reads the decision while the rule asks the very same question. Left out where none was read.
8 fields
answerstringrequiredThe answer chosen, one of the question's candidates - or
approved, for an approval.questionstring or nullThe question's id. Null for an approval, which answers none.
rulestring or nullThe rule that raises the question, as it asks it. Null for an approval.
decided_bystring or nullWho decided: a person in your account, by id.
method"sign_off" | "override" | "approval" or nullA sign-off by the person the question names, an admin's override of it, or the approval of the person the action's answer named, asked by Himmel.
resolution_idstring or nullThe decision's record, on the answer it was made on.
leads_tostring or nullWhat the answer led to:
continue- on to the rules after the question, which gave the outcome - or the outcome it gives itself. Null for an approval, read from the first rule.approvalstring or nullThe fact the decision stood in for as its decider's approval - where the action recorded none, and the rule that gave the outcome read it - so one person who decides and approves is asked once. Null otherwise.
recheckslist of Recheck or nullThe earlier answers this booking changed, each issued again before this one was given. Left out when it changed none, and on a proposal, which changes nothing until it is reported as executed.
4 fields
new_check_idstring or nullsupersedesstring or nullbooking_datestring or nulloutcomestring or null
supersedes_check_idstring or nullThe earlier answer for the same transaction that this one replaces.
recheck_reasonstring or nullWhy it was issued again: a later transaction, an action taken back (
reversal) or corrected (correction), a link or split between recipients, a question raised about its recipient, the rules changing, a question on it decided (decision), or the action approved by the person its answer named (approval).tracelist of TraceStep or nullEvery rule reached, in order. Left out when the check asked
explain: false.6 fields
rulestringrequiredstagestringrequiredscope,outcomeorconsequence.matchedboolean or nullresolveinteger or nullWhich of the rule's branches applied.
missinglist of string or nullnotestring or null
sandboxtrue or nulltrueon an answer given in test. A live answer has no such field.superseded_bystring or nullThe newer answer for the same transaction, when there is one.
resolutionResolution or nullThe latest decision your people recorded on it: a sign-off, an override, an approval or a refusal.
6 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
resolutionslist of Resolution or nullEvery decision recorded on it - sign-offs, overrides, approvals and refusals - in the order recorded: an override leaves the sign-off before it on record. Left out where there is none.
6 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
factsobject or nullThe facts the booking was sent with, as stored: a recipient by the id it resolved to when it was sent, never a name, and everything else as sent - from a key made for an agent, a person and an approval's figure as Himmel holds them. The booking's, so every answer to it shows the same; the answer's own
recipientsays which identity that answer used. Read back beside the answer, never part of it.sent_bySender or nullWho sent the check: a key, or a person signed in - beside who acted, in
transaction.actor. Empty on an answer Himmel issued again, which nobody asked for (recheck_reasonsays why), and on one a person sent before this was kept. Read back beside the stored answer, never part of it; the answer a check returns when it is made does not carry it.4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
executionExecution or nullFor an action asked about before it was taken: what became of it, and who reported it. The answer itself still says
proposed, which is what was answered.7 fields
status"proposed" | "executed" | "abandoned" | "expired"requiredproposeduntil its caller reports;executed, orabandonedwhen reported not taken;expiredwhen nobody reported within 24 hours. An action taken back since readsexecuted, as its report said:reversalsays the rest.executed_atstring or nullWhen it was taken, as reported.
reasonstring or nullWhy it was not taken:
abandoned,refused_by_caller,failedorexpired.refstring or nullYour own reference for the action taken, as its report sent it - kept beside the action's own
transaction.ref, never over it. Empty where the report sent none.systemstring or nullThe system the action is taken in, as its check or its report named it. Empty where neither did.
reported_bySender or nullWho reported it. Empty until somebody does, and for one that expired.
4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
reported_atstring or null
reversalReversal or nullFor an action taken back after it was taken, on every answer to it: when, your reference for it, and who reported it. It counts toward nothing from then; the answer itself stays as it was given.
4 fields
reversed_atstringrequiredWhen it was taken back, as reported.
refstring or nullYour own reference for taking it back, where you sent one.
reported_bySender or nullWho reported it: a key, or a person.
4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
reported_atstring or null
requestslist of AskedOfAPerson or nullWhom Himmel asked about this action, as each request stands now - for an action a key made for an agent asked about, each person an answer to it names: a policy's approver, a question's signer. Beside the answer, never part of it. Left out where nobody was asked.
11 fields
idstringrequiredThe request,
apr_…:GET /v1/approvals/{id}.asked_ofstringrequiredThe person asked: a person in your account, by Himmel id.
asked_atstringrequiredWhen Himmel asked them.
asks"approval" | "question"requiredWhat they are asked: their
approvalof the action, which a policy of yours asks of them - or their answer to the answer'squestion, which a sign-off gives.status"waiting" | "approved" | "refused" | "decided" | "lapsed"requiredwaitingfor them, a week at most;approvedorrefusedby them;decidedby a sign-off or an admin's override;lapsed,reasonsaying why.waits_untilstringrequiredUntil when it waits for its person: a week after it was asked, the proposal held open until then. One still waiting then lapses,
no_answer, and a decision after it is409 nothing-to-decide.decided_bystring or nullWho decided it, by Himmel id: the person asked, or the admin who overruled.
resolution_idstring or nullThe decision that answered it, on the answer it was recorded on.
closed_atstring or nullWhen it stopped waiting.
reasonstring or nullWhy it lapsed: the action
executed,abandonedorexpired;no_answerin a week;no_longer_asked, an answer issued again naming them no more;refused, another person refusing the action.emailed_atstring or nullWhen Himmel emailed them, linking the request's page in the console: within a minute of the request, in your account's own environment. Null until then, and where it never goes - a request made in test in an account with a live side, one that stopped waiting first, a person with no address, or one whose mail server refused their address.
The facts, under these rule versions, as of this date, give this outcome.
A rule covers this, and needs facts it did not get. missing names them.
missinglist of MissingFactrequired7 fields
factstringrequiredtypestring or nullcurrencystring or nullFor an amount, the currency it is wanted in: the rule set's. Send it there, or - for an amount in another currency - beside it, as what your own systems record it as in this one (
equivalents).descriptionstring or nullneeded_bystring or nullThe rule that needed it.
review_itemstring or nullFor
recipient_identity: the probable match to answer first.ofHistoryTransaction or nullWhere the fact is another action's: a booking a total counts and cannot read, by its id and its date. Send the fact by completing that booking (
transaction.completes), not in this check.2 fields
transaction_idstringrequiredeffective_datestring or null
No confirmed rule covers this: your policy does not say, or a rule awaits confirmation.
reason"awaiting_confirmation" | "rule_flagged" | "policy_gap" | "no_rule_matched" | "no_rule_set_for_jurisdiction" | "rule_set_broken"requireddetailstring or nullunconfirmed_ruleslist of string or null
A rule covers this, and turns on a word it cannot apply mechanically. A named person decides.
questionQuestionrequired5 fields
idstringrequiredcandidateslist of stringrequiredreasonstringrequiredWhy this one cannot be settled by the rule.
rulestringrequiredwordingstring or nullThe rule's wording, as your rule owner confirmed it.
signerSigner or null1 field
useranyrequiredThe person who signs off, as your settings name them.
- 401No API key was sent, or it is not in use (
unauthorized). - 403The caller may not do this, and
typesays why: the key or role lacks the scope, the rule set is not enabled for this account, the person signed in belongs to no account, names were asked for by a caller that is not shown them, or a request acting in test tried to change the account's settings (live-only). - 404Nothing with that id or name exists for this account.
- 409The request conflicts with what is stored: see
detail. - 413The request's body is larger than the operation takes, refused before any of it is read (
body-too-large): a policy's files at most 5 MB, any other body 1 MB - and an uploaded file at most 20 MB (file-too-large). - 422The request, or a fact in it, is not in the documented shape - a query parameter the operation does not take included, which is named (
unknown_parameter) rather than ignored - or itsHimmel-Envdoes not fit the caller. Missing facts are not errors. - 429More requests in a minute than the key's or the person's limit (
rate-limited) - or, opening an account, more accounts in a day than the deployment opens, withRetry-Aftersaying when to try again. - 500Something failed on our side.
X-Request-Ididentifies it in our logs.
/v1/checks/{check_id}/executionScope checks:writeWhat happened to a proposal
Report whether a proposed action was taken, on any answer to it - the one you kept from your first asking included, where you asked about it again. On executed: true the transaction joins history at that moment, and any earlier answer whose totals it moves is issued again with a check.rechecked alert. The action itself is answered again as it stands once taken: where a booking joined its totals, or rules or settings were confirmed, while it was asked about and it comes out otherwise, the new answer replaces the one given, and superseded_by names it; a change your rule owner has not confirmed yet never takes an answer away. On executed: false it is closed and never counts. A proposal with no report within 24 hours of its latest asking is closed as expired, unless a person Himmel asked about it holds it; a report after that is refused. Reporting the same outcome twice is answered the same way twice. ref is your own opaque reference for the action taken, kept beside the action's own transaction.ref and never over it: one that looks like an email address or an IBAN is refused with pii-in-facts. system is where the action was taken: the action's, or where it has none, named for it. A key made for an agent reports on its agent's own actions: on any other it gets 403 not-the-agents-action.
Parameters
check_idpath · stringrequiredIdempotency-Keyheader · string or nullMakes a retry safe: the same key with the same request within 24 hours gets the first answer again, and nothing is done twice. The same key with a different request is
409 idempotency-conflict. Keys are per environment and per path.
Request body · application/json
executedbooleanrequiredrefstring or nullYour own reference for the action taken - a credit note's number - opaque and at most 200 characters: one that looks like an email address or an IBAN is refused (
pii-in-facts). Kept beside the action's owntransaction.ref, never over it, asexecution.ref; the check log finds the action by either.systemstring or nullThe system the action was taken in, as on the check (
transaction.system): where the action has one, the same or left out - another is refused (changed) - and where it has none, named for the whole action by the first report.executed_atstring or nullWhen the action was done, a moment with its offset: it places the action in its day. Between when it was asked about and this report, a minute either side for clocks that differ; else
invalid-request. Left out, the moment of the report.reason"abandoned" | "refused_by_caller" | "failed" or null
Responses
200 Successful response
check_idstringrequiredtransaction_idstringrequiredstatus"executed" | "abandoned" | "expired"requiredreasonstring or nullexecuted_atstring or nullrefstring or nullThe reference kept for the action taken, beside the action's own - on a repeat, the first report's. Empty where none was sent, and for an action not taken.
systemstring or nullThe system the action is recorded as taken in now; empty where none was named.
recheck_jobstring or nullThe job re-checking earlier answers this execution moves.
superseded_bystring or nullThe answer that replaced the one reported on, as
GET /v1/checks/{id}reads it: where the action, answered again as it stood once taken, came out otherwise - a booking joined its totals, or rules or settings were confirmed, while it was asked about. Empty where the answer stands as it was given.
- 401No API key was sent, or it is not in use (
unauthorized). - 403The caller may not do this, and
typesays why: the key or role lacks the scope, the rule set is not enabled for this account, the person signed in belongs to no account, names were asked for by a caller that is not shown them, or a request acting in test tried to change the account's settings (live-only). - 404Nothing with that id or name exists for this account.
- 409The request conflicts with what is stored: see
detail. - 413The request's body is larger than the operation takes, refused before any of it is read (
body-too-large): a policy's files at most 5 MB, any other body 1 MB - and an uploaded file at most 20 MB (file-too-large). - 422The request, or a fact in it, is not in the documented shape - a query parameter the operation does not take included, which is named (
unknown_parameter) rather than ignored - or itsHimmel-Envdoes not fit the caller. Missing facts are not errors. - 429More requests in a minute than the key's or the person's limit (
rate-limited) - or, opening an account, more accounts in a day than the deployment opens, withRetry-Aftersaying when to try again. - 500Something failed on our side.
X-Request-Ididentifies it in our logs.
/v1/checks/{check_id}/reversalScope checks:writeAn action taken back
Report that an action taken was taken back afterwards - a credit given by mistake and clawed back, a refund whose payment failed. It counts toward nothing from then: every total it was in is worked out again without it, and each earlier answer that moves is issued again, with a check.rechecked alert where its state or outcome changed - those of actions that came while it stood included. Its own answers stay as they were given. Send it on any answer the action has: the check id you kept, or a later one. Take an action back this way, never by booking its negative, which counts as an action of its own. An action still being asked about is reported taken or not instead (/execution). Reporting it twice the same way is answered the same way twice. A key made for an agent takes back its agent's own actions: on any other it gets 403 not-the-agents-action.
Parameters
check_idpath · stringrequiredIdempotency-Keyheader · string or nullMakes a retry safe: the same key with the same request within 24 hours gets the first answer again, and nothing is done twice. The same key with a different request is
409 idempotency-conflict. Keys are per environment and per path.
Request body · application/json
reversed_atstring or nullWhen it was taken back: a moment with its offset, or a day. Left out, the moment the report is received.
refstring or nullYour own reference for taking it back, opaque and at most 200 characters: one that looks like an email address or an IBAN is refused (
pii-in-facts).
Responses
200 Successful response
check_idstringrequiredtransaction_idstringrequiredThe booking taken back: the one that stood for the action.
status"reversed"requiredreversed_atstringrequiredrefstring or nullrecheck_jobstring or nullThe job issuing again the answers it counted in; empty on a repeat of the same report, which queues nothing.
- 401No API key was sent, or it is not in use (
unauthorized). - 403The caller may not do this, and
typesays why: the key or role lacks the scope, the rule set is not enabled for this account, the person signed in belongs to no account, names were asked for by a caller that is not shown them, or a request acting in test tried to change the account's settings (live-only). - 404Nothing with that id or name exists for this account.
- 409The request conflicts with what is stored: see
detail. - 413The request's body is larger than the operation takes, refused before any of it is read (
body-too-large): a policy's files at most 5 MB, any other body 1 MB - and an uploaded file at most 20 MB (file-too-large). - 422The request, or a fact in it, is not in the documented shape - a query parameter the operation does not take included, which is named (
unknown_parameter) rather than ignored - or itsHimmel-Envdoes not fit the caller. Missing facts are not errors. - 429More requests in a minute than the key's or the person's limit (
rate-limited) - or, opening an account, more accounts in a day than the deployment opens, withRetry-Aftersaying when to try again. - 500Something failed on our side.
X-Request-Ididentifies it in our logs.
/v1/checks/{check_id}Scope checks:readOne stored answer, exactly as it was given
The answer as it was stored when it was given, for the whole retention period, with superseded_by when a later answer replaced it, and resolutions, every sign-off or override your people recorded, resolution the latest; facts, what the booking was sent with, a recipient by its id; sent_by, who sent the check - a key or a person signed in, beside who acted - and, for an action asked about before it was taken, execution: what became of it, and who reported it; for an action taken back, on every answer to it, reversal: when, and who reported it; and requests, whom Himmel asked about the action, as each request stands now.
Needs a key with the scope checks:read. From a key made for an agent it reads that agent's own alone: another agent's action is 403 not-the-agents-action, another agent's record 403 insufficient-scope.
Parameters
check_idpath · stringrequired
Responses
200 Successful response
One of 4, told apart by state.
effective_datestringrequiredThe date the rules were applied as of.
as_ofstringrequiredThe moment the history was read.
layersLayersrequired2 fields
lawLayerAnswer or nullThe law's answer. Left out where no law set is part of the answer - your policy's alone, for a country we hold no law for or one your account has not switched on.
12 fields
rule_setstringrequiredrule_versionstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredoutcomestring or nullbasisstring or nulllaw,policyorgoodwill.consequenceslist of Consequence or null5 fields
keystringrequiredvalueanyrequiredMoney as minor units and currency, or a number, a flag or a date.
rulestringrequiredbasisstring or nulllaw,policyorgoodwill.citationCitation or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
approveranyWho a policy says must approve, where it names one.
handed_toanyWhom a policy hands the action to, where its rule names one: a person in your account, by Himmel id. Where an agent asked about the action, Himmel tells them (
check.handed_over).tenant_facts_usedlist of string or nullYour settings this answer read.
reference_versionsmap of integer | string or nullWhich version of each reference table it read, such as dated VAT rates.
waiting_onstring or nullA policy rule that reads the law's answer waits for the law to settle:
law.
policieslist of LayerAnswer or nullYour policies for this domain, in the order they ran.
12 fields
rule_setstringrequiredrule_versionstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredoutcomestring or nullbasisstring or nulllaw,policyorgoodwill.consequenceslist of Consequence or null5 fields
keystringrequiredvalueanyrequiredMoney as minor units and currency, or a number, a flag or a date.
rulestringrequiredbasisstring or nulllaw,policyorgoodwill.citationCitation or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
approveranyWho a policy says must approve, where it names one.
handed_toanyWhom a policy hands the action to, where its rule names one: a person in your account, by Himmel id. Where an agent asked about the action, Himmel tells them (
check.handed_over).tenant_facts_usedlist of string or nullYour settings this answer read.
reference_versionsmap of integer | string or nullWhich version of each reference table it read, such as dated VAT rates.
waiting_onstring or nullA policy rule that reads the law's answer waits for the law to settle:
law.
engine_versionstringrequiredcheck_idstringrequiredThe receipt.
GET /v1/checks/{id}gives this answer back.transactionTransactionSummaryrequired9 fields
transaction_idstringrequiredstatus"executed" | "proposed"requiredproposedstays out of every total until you report that it executed.channel"card" | "invoice" | "reimbursement" | "booking" | "agent"requiredrefstring or nullYour own reference, as you sent it.
runstring or nullThe run of an agent the action is part of, as your systems identify it.
systemstring or nullThe system the action is taken in, by your company's code for it.
actorActor or null3 fields
kind"user" | "agent" | "system"requiredidstring or nullon_behalf_ofstring or nullThe person an agent acted for.
completesstring or nullThe booking whose missing facts this check sent, where it completed one: the same action, which this booking stands in for in every total.
correctsstring or nullThe booking whose facts sent wrong this check corrected, where it corrected one: the same action, as it should have been, which this booking stands in for in every total.
created_atstringrequiredrecipientRecipientSummary or null4 fields
resolutionstringrequiredresolved,unresolved,probable_matchormissing.recipient_idstring or nullreview_itemstring or nullThe open probable match, where there is one.
fy_totalMoney or nullThis recipient's total for the fiscal year, where the answer's first rule set totals money per recipient per fiscal year: the recipient's other bookings of the year that it counts, as known when the answer was given - this one not included, and later ones too where it counts the whole year. Left out where no such total was read;
history_usednames every total an answer read.2 fields
amountintegerrequiredcurrencystringrequired
history_usedlist of HistoryInput or null5 fields
rule_setstringrequiredinputstringrequiredThe rule set's name for this total, such as
recipient_fy_gifts.countintegerrequiredtransactionslist of HistoryTransactionrequired2 fields
transaction_idstringrequiredeffective_datestring or null
totalMoney | integer | string or null
not_checkedlist of NotChecked or null2 fields
topicstringrequiredreasonstringrequired
citationslist of Citation or null4 fields
sourcestringrequiredThe source's key within the rule set, such as
estg.paragraphstring or nullclausestring or nulltext_refstring or nullThe passage in the rule set's own copy of the source,
sources/<file>#<anchor>.
confirmationslist of ConfirmationUsed or null3 fields
rulestringrequiredconfirmed_bystring or nullconfirmed_atstring or null
decisionslist of DecisionRead or nullEach question on this action a person decided, read as its answer: where the question's rule says what each answer leads to, a sign-off or an admin's override issues the action's answer again, and every answer to it after reads the decision while the rule asks the very same question. Left out where none was read.
8 fields
answerstringrequiredThe answer chosen, one of the question's candidates - or
approved, for an approval.questionstring or nullThe question's id. Null for an approval, which answers none.
rulestring or nullThe rule that raises the question, as it asks it. Null for an approval.
decided_bystring or nullWho decided: a person in your account, by id.
method"sign_off" | "override" | "approval" or nullA sign-off by the person the question names, an admin's override of it, or the approval of the person the action's answer named, asked by Himmel.
resolution_idstring or nullThe decision's record, on the answer it was made on.
leads_tostring or nullWhat the answer led to:
continue- on to the rules after the question, which gave the outcome - or the outcome it gives itself. Null for an approval, read from the first rule.approvalstring or nullThe fact the decision stood in for as its decider's approval - where the action recorded none, and the rule that gave the outcome read it - so one person who decides and approves is asked once. Null otherwise.
recheckslist of Recheck or nullThe earlier answers this booking changed, each issued again before this one was given. Left out when it changed none, and on a proposal, which changes nothing until it is reported as executed.
4 fields
new_check_idstring or nullsupersedesstring or nullbooking_datestring or nulloutcomestring or null
supersedes_check_idstring or nullThe earlier answer for the same transaction that this one replaces.
recheck_reasonstring or nullWhy it was issued again: a later transaction, an action taken back (
reversal) or corrected (correction), a link or split between recipients, a question raised about its recipient, the rules changing, a question on it decided (decision), or the action approved by the person its answer named (approval).tracelist of TraceStep or nullEvery rule reached, in order. Left out when the check asked
explain: false.6 fields
rulestringrequiredstagestringrequiredscope,outcomeorconsequence.matchedboolean or nullresolveinteger or nullWhich of the rule's branches applied.
missinglist of string or nullnotestring or null
sandboxtrue or nulltrueon an answer given in test. A live answer has no such field.superseded_bystring or nullThe newer answer for the same transaction, when there is one.
resolutionResolution or nullThe latest decision your people recorded on it: a sign-off, an override, an approval or a refusal.
6 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
resolutionslist of Resolution or nullEvery decision recorded on it - sign-offs, overrides, approvals and refusals - in the order recorded: an override leaves the sign-off before it on record. Left out where there is none.
6 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
factsobject or nullThe facts the booking was sent with, as stored: a recipient by the id it resolved to when it was sent, never a name, and everything else as sent - from a key made for an agent, a person and an approval's figure as Himmel holds them. The booking's, so every answer to it shows the same; the answer's own
recipientsays which identity that answer used. Read back beside the answer, never part of it.sent_bySender or nullWho sent the check: a key, or a person signed in - beside who acted, in
transaction.actor. Empty on an answer Himmel issued again, which nobody asked for (recheck_reasonsays why), and on one a person sent before this was kept. Read back beside the stored answer, never part of it; the answer a check returns when it is made does not carry it.4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
executionExecution or nullFor an action asked about before it was taken: what became of it, and who reported it. The answer itself still says
proposed, which is what was answered.7 fields
status"proposed" | "executed" | "abandoned" | "expired"requiredproposeduntil its caller reports;executed, orabandonedwhen reported not taken;expiredwhen nobody reported within 24 hours. An action taken back since readsexecuted, as its report said:reversalsays the rest.executed_atstring or nullWhen it was taken, as reported.
reasonstring or nullWhy it was not taken:
abandoned,refused_by_caller,failedorexpired.refstring or nullYour own reference for the action taken, as its report sent it - kept beside the action's own
transaction.ref, never over it. Empty where the report sent none.systemstring or nullThe system the action is taken in, as its check or its report named it. Empty where neither did.
reported_bySender or nullWho reported it. Empty until somebody does, and for one that expired.
4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
reported_atstring or null
reversalReversal or nullFor an action taken back after it was taken, on every answer to it: when, your reference for it, and who reported it. It counts toward nothing from then; the answer itself stays as it was given.
4 fields
reversed_atstringrequiredWhen it was taken back, as reported.
refstring or nullYour own reference for taking it back, where you sent one.
reported_bySender or nullWho reported it: a key, or a person.
4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
reported_atstring or null
requestslist of AskedOfAPerson or nullWhom Himmel asked about this action, as each request stands now - for an action a key made for an agent asked about, each person an answer to it names: a policy's approver, a question's signer. Beside the answer, never part of it. Left out where nobody was asked.
11 fields
idstringrequiredThe request,
apr_…:GET /v1/approvals/{id}.asked_ofstringrequiredThe person asked: a person in your account, by Himmel id.
asked_atstringrequiredWhen Himmel asked them.
asks"approval" | "question"requiredWhat they are asked: their
approvalof the action, which a policy of yours asks of them - or their answer to the answer'squestion, which a sign-off gives.status"waiting" | "approved" | "refused" | "decided" | "lapsed"requiredwaitingfor them, a week at most;approvedorrefusedby them;decidedby a sign-off or an admin's override;lapsed,reasonsaying why.waits_untilstringrequiredUntil when it waits for its person: a week after it was asked, the proposal held open until then. One still waiting then lapses,
no_answer, and a decision after it is409 nothing-to-decide.decided_bystring or nullWho decided it, by Himmel id: the person asked, or the admin who overruled.
resolution_idstring or nullThe decision that answered it, on the answer it was recorded on.
closed_atstring or nullWhen it stopped waiting.
reasonstring or nullWhy it lapsed: the action
executed,abandonedorexpired;no_answerin a week;no_longer_asked, an answer issued again naming them no more;refused, another person refusing the action.emailed_atstring or nullWhen Himmel emailed them, linking the request's page in the console: within a minute of the request, in your account's own environment. Null until then, and where it never goes - a request made in test in an account with a live side, one that stopped waiting first, a person with no address, or one whose mail server refused their address.
The facts, under these rule versions, as of this date, give this outcome.
A rule covers this, and needs facts it did not get. missing names them.
missinglist of MissingFactrequired7 fields
factstringrequiredtypestring or nullcurrencystring or nullFor an amount, the currency it is wanted in: the rule set's. Send it there, or - for an amount in another currency - beside it, as what your own systems record it as in this one (
equivalents).descriptionstring or nullneeded_bystring or nullThe rule that needed it.
review_itemstring or nullFor
recipient_identity: the probable match to answer first.ofHistoryTransaction or nullWhere the fact is another action's: a booking a total counts and cannot read, by its id and its date. Send the fact by completing that booking (
transaction.completes), not in this check.2 fields
transaction_idstringrequiredeffective_datestring or null
No confirmed rule covers this: your policy does not say, or a rule awaits confirmation.
reason"awaiting_confirmation" | "rule_flagged" | "policy_gap" | "no_rule_matched" | "no_rule_set_for_jurisdiction" | "rule_set_broken"requireddetailstring or nullunconfirmed_ruleslist of string or null
A rule covers this, and turns on a word it cannot apply mechanically. A named person decides.
questionQuestionrequired5 fields
idstringrequiredcandidateslist of stringrequiredreasonstringrequiredWhy this one cannot be settled by the rule.
rulestringrequiredwordingstring or nullThe rule's wording, as your rule owner confirmed it.
signerSigner or null1 field
useranyrequiredThe person who signs off, as your settings name them.
- 401No API key was sent, or it is not in use (
unauthorized). - 403The caller may not do this, and
typesays why: the key or role lacks the scope, the rule set is not enabled for this account, the person signed in belongs to no account, names were asked for by a caller that is not shown them, or a request acting in test tried to change the account's settings (live-only). - 404Nothing with that id or name exists for this account.
- 422The request, or a fact in it, is not in the documented shape - a query parameter the operation does not take included, which is named (
unknown_parameter) rather than ignored - or itsHimmel-Envdoes not fit the caller. Missing facts are not errors. - 429More requests in a minute than the key's or the person's limit (
rate-limited) - or, opening an account, more accounts in a day than the deployment opens, withRetry-Aftersaying when to try again. - 500Something failed on our side.
X-Request-Ididentifies it in our logs.
/v1/checksScope checks:readThe check log
Your checks, newest first, fifty to a page. Continue with before set to the last page's next_before. Every answer ever given is here, including those a later answer replaced; current=true keeps only the answers that stand, one per booking. Each row says who acted and, in sent_by, who sent the check: a key, or a person signed in; that a later answer replaced it (superseded_by), what your people decided (resolution), and who approves or decides (a layer's approver, a contested answer's signer); and its transaction what is the action's rather than the answer's: who sent it, the latest decision on any of its answers, and the facts it was sent with. order=effective_date lists by the day each action happened, so an answer issued again never moves its row. Find an action by your own reference (ref), a booking's answers by its id (transaction), the actions of some days by the day they happened (effective_from, effective_to), a customer's by any of its records (recipient), who acted - one or several (actor), or every agent (actor_kind) - and for whom (on_behalf_of), an agent's run by its id (run), the actions taken in a system by its code (system), and the actions Himmel asked a person about by where each request stands (request) and whom it asked (asked_of); each row's transaction.requests says whom it asked. answer keeps the answers of a kind and against_rules=true the actions taken against your rules, as GET /v1/map counts them, each of its figures linking here. waiting=true keeps what waits on a person, which GET /v1/review counts in groups; reason finds why an answer is not covered and question the question it asks. state, outcome, policy_outcome and reason take several values, separated by commas. A group of the review queue links to the query here that lists its answers, and a checks.rerun alert to the answers it issued again.
Needs a key with the scope checks:read. From a key made for an agent it reads that agent's own alone: another agent's action is 403 not-the-agents-action, another agent's record 403 insufficient-scope.
Parameters
statequery · string or nullOne state, or several separated by commas.
rule_setquery · string or nullOnly the checks that named this rule set.
recipientquery · string or nullOnly the checks of this customer, by the id of any of its records: every record you answered to be the same, or a confirmed matching rule linked, counts as one, as a yearly total counts them. A record an open question only proposes does not.
actorquery · string or nullOnly what this person or agent did, by the id the check named - or any of several, separated by commas: the agents one person answers for, say.
actor_kindquery · string or nullOnly what this kind of actor did, as the check named it (
transaction.actor.kind):agent,userorsystem- or several, separated by commas.actor_kind=agentis what every agent did, which the map counts. Any other value is refused (enum).on_behalf_ofquery · string or nullOnly the actions done for this person, by the id the checks named as
actor.on_behalf_of, exactly: your own id for them, or a colleague's Himmel id.runquery · string or nullOnly the actions of this run of an agent, by the
transaction.runtheir checks named, exactly - withactor, one agent's run.systemquery · string or nullOnly the actions taken in this system, by the code their check or report named (
transaction.system) -nonefor those whose system nobody named - or several, separated by commas. A value that is no system's code is refused (format).transaction_statusquery · string or nullexecuted,proposed,abandoned,expiredorreversed(taken back): what became of the booking.requestquery · string or nullOnly the actions about which Himmel asked a person something whose request is now
waiting,approved,refused,decidedorlapsed- one, or several separated by commas. Withwaiting=true,request=waitingis the review queue's approvals.asked_ofquery · string or nullOnly the actions Himmel asked this person about, by their Himmel id - with
request, a request of theirs that now stands so:asked_of=usr_…&request=waitingis what waits on them.refquery · string or nullOnly the bookings sent with this
transaction.ref, your own reference, or reported taken with it (execution.ref), exactly.transactionquery · string or nullOnly the answers to this booking, by its
transaction_id- every one, those a later answer replaced included, unlesscurrent=true.effective_fromquery · string or nullOnly the actions that happened on this day or after, as each answer is dated (
effective_date): the day its facts give, or where they give none the day it was answered. A date,YYYY-MM-DD.effective_toquery · string or nullOnly the actions that happened on this day or before. A date,
YYYY-MM-DD.outcomequery · string or nullOne outcome, or several separated by commas.
policy_outcomequery · string or nullWhat a policy of yours answered - one outcome, or several separated by commas - whatever the answer as a whole says. Where our law answers too,
outcomeis the law's, so a gift your policy does not allow is found withpolicy_outcome=not_allowed.answerquery · string or nullOnly the answers of this kind - or of several, separated by commas - each read as the map counts it (
GET /v1/map): by its first policy's layer where a policy answered, and otherwise by the answer itself.allowed: determined, letting the action stand;approval:needs_approval;person: handed to a person, orcontested;refused:not_allowed;other: an outcome a policy declares in its own word;open: not covered, or a fact missing. Any other value is refused (enum).against_rulesquery · booleanOnly the actions taken against your rules, as
check.against_rulesis raised: reported taken, with an answer one of whose policies is determined with an outcome that does not let the action stand - any butallowedandnot_applicable.currentquery · booleanOnly the answers that stand: none given later replaced them.
waitingquery · booleanOnly the answers that stand and wait on a person at your company: a question nobody has decided, an answer your rules do not give yet, and facts missing from an action that was taken - or from any action, a "same customer?" question. Never an action that was not taken. What
GET /v1/reviewcounts.reasonquery · string or nullOnly the
not_coveredanswers not covered for this reason -policy_gap,awaiting_confirmation,rule_flagged,no_rule_matched,no_rule_set_for_jurisdictionorrule_set_broken- or several, separated by commas.questionquery · string or nullOnly the
contestedanswers that ask this question, by itsid.missingquery · string or nullOnly answers that name this fact as missing, e.g.
recipient.consequencequery · string or nullOnly answers that carry this consequence, e.g.
non_deductible_amount.recheckedquery · booleanOnly answers that replaced an earlier one: re-checks.
rerunquery · string or nullOnly the answers one re-run issued again, by the
rerun_iditschecks.rerunalert names: the answers a decision about the rules changed.fromquery · string or nullOnly the answers given at or after this moment, an ISO 8601 timestamp or a date - a date alone is its midnight, UTC. When the action happened is
effective_from.toquery · string or nullOnly the answers given before this moment, which is not included; as
from.limitquery · integerHow many to a page.
beforequery · string or nullThe last page's
next_before, to continue from.orderquery · "created_at" | "effective_date"created_at, the default: newest answer first.effective_date: by the day each action happened, newest first - within a day the one first asked about or booked last first - so an answer issued again never moves its row; withcurrent=true, one row per action.
Responses
200 Successful response
checkslist of CheckLogRowrequired20 fields
check_idstringrequiredcreated_atstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredrule_setstringrequiredeffective_datestringrequiredas_ofstringrequiredenv"live" | "test"requiredconsequenceslist of ConsequencerequiredEvery layer's consequences, law first.
5 fields
keystringrequiredvalueanyrequiredMoney as minor units and currency, or a number, a flag or a date.
rulestringrequiredbasisstring or nulllaw,policyorgoodwill.citationCitation or null4 fields
sourcestringrequiredThe source's key within the rule set, such as
estg.paragraphstring or nullclausestring or nulltext_refstring or nullThe passage in the rule set's own copy of the source,
sources/<file>#<anchor>.
layerslist of CheckLogLayerrequiredWhat each rule set made of it, law first. Where our law answers too,
outcomeis the law's, and what your policy said is here. Empty for an answer given before any rule ran.7 fields
kindstringrequiredlaworpolicy.rule_setstringrequiredrule_versionstringrequiredstate"determined" | "undetermined_missing_facts" | "not_covered" | "contested"requiredoutcomestring or nullrulestring or nullThe rule whose branch gave this layer its outcome or its question. Empty where it waits on facts -
missingnames the rules that asked - or is not covered.approverstring or nullWho this layer's answer says must approve - where it
needs_approval, the person your settings name for it, by Himmel id. Empty where it names nobody.
transactionCheckLogTransactionrequired18 fields
transaction_idstringrequiredstatusstringrequiredexecuted,proposed,abandonedorexpired- orreversed, an action taken back after it was taken, which counts toward nothing.measureslist of MeasurerequiredWhat the action came to in another measure than money: each whole number a figure of its rule set marks as approved covers - a retention discount's percentage, where its policy marks
approved_percent: { type: int, approval: discount_percent }- read from the versions that answered it, in their order, each fact once. Empty where none marks one or the booking says none.2 fields
factstringrequiredThe fact, as the rule set declares it:
discount_percent.valueintegerrequiredWhat the action sent for it:
20.
actorCheckLogActorrequired3 fields
kindstring or nullidstring or nullon_behalf_ofstring or null
factsobjectrequiredThe facts the booking was sent with, as stored: a recipient by the id it resolved to, never a name, and everything else as sent - from a key made for an agent, a person and an approval's figure as Himmel holds them.
refstring or nullThe action's own reference, as its check sent it.
executed_refstring or nullYour own reference for the action taken, as its report sent it, beside the action's own:
ref=finds the action by either. Empty where its report sent none.recipient_idstring or nullbooking_datestring or nullamountBookedMoney or nullWhat the booking was for: the fact its rule set names as its amount (
amount_fact) - for our German gifts rule setamount_gross, what it was booked at, tax included - with the figures sent beside it (equivalents). Empty where the rule set names none or the booking says none.3 fields
amountintegerrequiredcurrencystringrequiredequivalentslist of Money or nullWhat the company's own systems record it as in other currencies, as sent beside it - a figure per currency, which a rule set in that currency reads. Never a rate we picked.
runstring or nullThe run of an agent the action was part of, where its check named one.
systemstring or nullThe system the action was taken in, by your company's code for it, as its check or a request after it named it: what
system=finds. Empty where nobody named one.sent_bySender or nullWho sent the action: the key or the person that sent its first answer - for an action whose facts a later check completed, whoever sent its first booking. The row's own
sent_byis the answer's, empty on an answer issued again.4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
completesstring or nullThe booking whose missing facts this booking's check sent, where it completed one: the same action, which this booking stands in for in every total.
correctsstring or nullThe booking whose facts sent wrong this booking's check corrected, where it corrected one: the same action, which this booking stands in for in every total.
reversed_atstring or nullWhen the action was taken back, where it was: its
statusis thenreversed.resolutionBookingResolution or nullThe latest decision - a sign-off, an override, an approval or a refusal - recorded on any answer to this action's bookings, and the answer it was recorded on - an earlier one where the answer was issued again since asking another question: a decision is carried over only where the same question is asked again.
7 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
check_idstringrequiredThe answer it was recorded on.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
requestslist of AskedOfAPerson or nullWhom Himmel asked about this action, as each request stands now - for an action a key made for an agent asked about, each person an answer to it names: a policy's approver, a question's signer. Beside the answer, never part of it. Left out where nobody was asked.
11 fields
idstringrequiredThe request,
apr_…:GET /v1/approvals/{id}.asked_ofstringrequiredThe person asked: a person in your account, by Himmel id.
asked_atstringrequiredWhen Himmel asked them.
asks"approval" | "question"requiredWhat they are asked: their
approvalof the action, which a policy of yours asks of them - or their answer to the answer'squestion, which a sign-off gives.status"waiting" | "approved" | "refused" | "decided" | "lapsed"requiredwaitingfor them, a week at most;approvedorrefusedby them;decidedby a sign-off or an admin's override;lapsed,reasonsaying why.waits_untilstringrequiredUntil when it waits for its person: a week after it was asked, the proposal held open until then. One still waiting then lapses,
no_answer, and a decision after it is409 nothing-to-decide.decided_bystring or nullWho decided it, by Himmel id: the person asked, or the admin who overruled.
resolution_idstring or nullThe decision that answered it, on the answer it was recorded on.
closed_atstring or nullWhen it stopped waiting.
reasonstring or nullWhy it lapsed: the action
executed,abandonedorexpired;no_answerin a week;no_longer_asked, an answer issued again naming them no more;refused, another person refusing the action.emailed_atstring or nullWhen Himmel emailed them, linking the request's page in the console: within a minute of the request, in your account's own environment. Null until then, and where it never goes - a request made in test in an account with a live side, one that stopped waiting first, a person with no address, or one whose mail server refused their address.
outcomestring or nullmissinglist of MissingFact or nullWhat an
undetermined_missing_factsanswer asks for, as the answer names it.7 fields
factstringrequiredtypestring or nullcurrencystring or nullFor an amount, the currency it is wanted in: the rule set's. Send it there, or - for an amount in another currency - beside it, as what your own systems record it as in this one (
equivalents).descriptionstring or nullneeded_bystring or nullThe rule that needed it.
review_itemstring or nullFor
recipient_identity: the probable match to answer first.ofHistoryTransaction or nullWhere the fact is another action's: a booking a total counts and cannot read, by its id and its date. Send the fact by completing that booking (
transaction.completes), not in this check.2 fields
transaction_idstringrequiredeffective_datestring or null
reason"awaiting_confirmation" | "rule_flagged" | "policy_gap" | "no_rule_matched" | "no_rule_set_for_jurisdiction" | "rule_set_broken" or nullWhy a
not_coveredanswer is not covered.questionQuestion or nullThe question a
contestedanswer puts to a named person.5 fields
idstringrequiredcandidateslist of stringrequiredreasonstringrequiredWhy this one cannot be settled by the rule.
rulestringrequiredwordingstring or nullThe rule's wording, as your rule owner confirmed it.
signerSigner or nullWho decides a
contestedanswer's question: the person your settings name.1 field
useranyrequiredThe person who signs off, as your settings name them.
supersedes_check_idstring or nullThe earlier answer for the same booking that this one replaced.
superseded_bystring or nullThe newer answer for the same booking that replaced this one, where one did.
recheck_reasonstring or nullsent_bySender or nullWho sent the check: a key, or a person signed in - beside who acted, in
transaction.actor. Empty on an answer Himmel issued again, and on one a person sent before this was kept.4 fields
kind"api_key" | "person"requiredidstringrequiredThe key's id, or the person's user id.
prefixstring or nullA key's prefix,
hk_live_7f3a: what your keys show, never the key itself.namestring or nullThe name the key was given, where it was given one.
resolutionResolution or nullThe latest decision your people recorded on this answer - a sign-off, an override, an approval or a refusal;
GET /v1/checks/{id}gives every one.6 fields
resolved_outcomestringrequiredOne of the question's answers, or of the outcomes its rule set declares - or for an approval Himmel asked for,
approvedorrefused.resolved_bystringrequiredmethod"sign_off" | "override" | "approval"requiredA sign-off by the person a question names, an admin's override, or the approval or refusal of the person Himmel asked (
POST /v1/approvals/{id}/approve).created_atstringrequiredWhen it was decided.
note_refstring or nullgiven_onstring or nullWhere the decision was carried over from an earlier answer that asked the very same question: the answer it was given on. Left out for a decision given on this answer.
next_beforestring or nullPass as
beforefor the next page; empty on the last one.
- 401No API key was sent, or it is not in use (
unauthorized). - 403The caller may not do this, and
typesays why: the key or role lacks the scope, the rule set is not enabled for this account, the person signed in belongs to no account, names were asked for by a caller that is not shown them, or a request acting in test tried to change the account's settings (live-only). - 422The request, or a fact in it, is not in the documented shape - a query parameter the operation does not take included, which is named (
unknown_parameter) rather than ignored - or itsHimmel-Envdoes not fit the caller. Missing facts are not errors. - 429More requests in a minute than the key's or the person's limit (
rate-limited) - or, opening an account, more accounts in a day than the deployment opens, withRetry-Aftersaying when to try again. - 500Something failed on our side.
X-Request-Ididentifies it in our logs.