Skip to content

Verification metadata

POST /v1/sessions accepts a metadata object. It is your own key/value data. The platform stores it with the verification and gives your keys back unchanged on a read. A read can also carry keys the platform added later: see Reading the values back.

The platform understands five key names inside it. Together they state when the transaction starts and ends, its amount and currency, and where it happens. A document the applicant signs can print each one into its text. Every other key is yours, and the platform never interprets it.

{
"applicant_id": "app_01hqz8w7j1l2m3n4p5q6r7s8t9",
"workflow_template_id": "wt_01hqz9x8k2m3n4p5q6r7s8t9u0",
"metadata": {
"start_datetime": "2026-03-14T15:00:00-05:00",
"end_datetime": "2026-03-17T11:00:00-05:00",
"amount": 125000,
"currency": "usd",
"location": "12 Main St, Austin, TX 78701",
"your_booking_ref": "BK-4471"
}
}

Each one is optional. If you send one in the wrong shape, the platform refuses the whole request with 422 validation_error.

Key Format Refused example
start_datetime ISO 8601 with an explicit offset: 2026-03-14T15:00:00-07:00 or 2026-03-14T22:00:00Z. "2026-03-14"
end_datetime The same format. It must fall at or after start_datetime. "2026-03-14T15:00:00"
amount A whole number in the minor units of currency, from 0 to 999999999999. It requires currency. "1250.00"
currency A three-letter ISO 4217 code. The platform keeps the case you send. "US Dollars"
location One display line of 500 characters or less. No control characters, no bidirectional marks, no angle brackets. "<b>Suite 4</b>"

amount is in minor units, and no check can find a mistake here

Section titled “amount is in minor units, and no check can find a mistake here”

Send the amount the way the currency counts it, with no decimal point:

  • 125000 with "currency": "usd" prints $1,250.00.
  • 1250 with "currency": "usd" prints $12.50. That is a valid request, and $12.50 is what the document shows.
  • 50000 with "currency": "jpy" prints ¥50,000. A currency with no decimal places takes the same whole number and applies no scaling, so do not multiply by 100 for it.

The wire carries no unit and the platform applies no plausible-range check. A major-unit value is therefore accepted, and it prints 100 times too small on a document that a person signs. Convert once, near the place where you read the price, and test the number that reaches the document.

You can send currency on its own. Always send currency with amount: the platform refuses an amount alone, because it cannot print a number with no currency.

The offset you send is the time zone the document prints

Section titled “The offset you send is the time zone the document prints”

Both datetimes need an explicit offset. A bare local datetime (2026-03-14T15:00:00) and a date alone (2026-03-14) are both refused. The value prints into a sealed document that is never rendered again, and an unlabeled instant reads as the wrong calendar date on either side of midnight.

Send the offset that applies where the transaction happens, not the offset where your systems run. 2026-03-14T15:00:00-07:00 prints as 2026-03-14 15:00 (UTC-07:00).

A value at exactly midnight in the offset you send prints as the date alone, with no clock. This is deliberate. A source system that records only a calendar date has no hour to state, and a printed 00:00 would read as a real start time. The offset decides this, not the instant: 2026-03-17T00:00:00Z prints a bare date, and 2026-03-16T19:00:00-05:00 is the same moment but prints a clock.

The platform compares the two values as instants, so a pair written in different offsets still compares correctly.

A document can place any of five variables, which the document editor offers by name. Each one reads a single key from this object:

Variable in the document Key you send
Start date and time start_datetime
End date and time end_datetime
Transaction amount amount
Currency currency
Location location

A document that places one of these makes the matching key required. If the workflow template you create against runs a document signing step whose document places Transaction amount, a create with no metadata.amount is refused:

{
"error": {
"code": "esign_requirements_not_met",
"message": "The document uses \"Transaction amount\", which comes from the session metadata. Add \"amount\" to the metadata when you create the session, or remove those fields from the document.",
"details": { "reason": "esign_session_metadata_required" }
}
}

Every error also carries type, request_id, and doc_url. Those are the same on every response, and the Error reference shows them in full, so the examples on this page show only the fields that differ.

The status is 403 and details.reason is esign_session_metadata_required. The platform creates nothing. The message names every missing variable and every key to send, so you can show it to whoever operates your integration.

An empty string and null do not reach this refusal. Each of the five has a shape, so "" and null fail that shape and come back as 422 validation_error well before the document is consulted. Handle the 422 for a field you send empty, and the 403 above only for a field you omit.

This refusal cannot come any earlier. The value comes from the individual create call, and no saved workflow template knows it, so a workflow that uses these variables saves normally and only the create is refused. Every other esign_requirements_not_met reason describes the workflow or the document instead, and a template save is refused for it. See esign_requirements_not_met.

The platform writes to the same object, so it refuses seven key names:

referenceDetails, notes, operatorNotes, cancellationReason, manualReviewReason, erroredReason, __requested_checks.

A request that sends one gets 422 validation_error naming the key. Rename it: notes becomes your_notes.

The platform accepted these key names before this release. A key that your integration sends today can be refused after you upgrade, so search your integration for all seven first.

Six of the seven were stored, and platform surfaces then read them. That is the reason for the change. A value you wrote under operatorNotes appeared to a reviewer as a note from your own team. A value you wrote under erroredReason reached the error boundary that decides how a stalled verification is shown. The seventh, __requested_checks, was accepted and then discarded, so nothing you sent under it was ever readable.

Three rules apply to the whole object, not to one field: the reserved-key refusal, amount needing currency, and end_datetime not preceding start_datetime. There are two consequences.

  • These three rules do not appear in the OpenAPI schema. A generated schema can describe the shape of one field, but not a rule that spans two of them. The three rules are therefore published as prose, here and in the metadata description in the interactive reference.
  • error.param is metadata for all three. The platform derives param from the first segment of the issue path, so param alone cannot tell the three rules apart. Read details.issues[].path, which carries the full path, and show the issue message, which names the key at fault.
{
"error": {
"code": "validation_error",
"message": "Invalid request body",
"param": "metadata",
"details": {
"issues": [
{
"code": "custom",
"path": ["metadata", "currency"],
"message": "Provide \"currency\" alongside \"amount\" so the amount can be shown with its currency."
}
]
}
}
}

GET /v1/sessions/{id} returns your own keys unchanged: the same snake_case names, the same capitalization on currency, and the same offsets on the datetimes. The platform rewrites one value and only one. It removes leading and trailing spaces from location, and the trimmed value is what you read back.

The object you read is not always the object you sent. The platform writes into the same object as the verification progresses, and those keys are returned to you along with your own. A verification that an operator canceled carries cancellationReason; one an operator sent to review carries manualReviewReason; one that stalled carries erroredReason; and a note added through the API or the console carries operatorNotes. None of them is present at create, and none is removed on read.

So do not post a metadata object you read back on a create. Before this release you could read a verification’s metadata, merge your changes into it, and post the result as the metadata of the next verification. That now fails with 422 validation_error, because the platform refuses every key in the list above on the way in. Build the object you post from your own data, or strip the reserved names before you send it.

Terminal window
curl -s https://api-dev.us.autohost-dev.uk/v1/sessions/vs_01H... \
-H "Authorization: Bearer ah_sk_us_live_<your-key>" \
| jq .data.metadata

A verification that a connected system starts can fill part of this object automatically. Which fields arrive depends on the system, and some supply none of them.

No integration supplies amount, currency, or location. A document that places Transaction amount, Currency, or Location therefore refuses every verification an integration tries to start. The response is the same esign_session_metadata_required, and no applicant is invited.

  • The Hostaway integration supplies start_datetime and end_datetime, and only when the reservation webhook carries the time zone of the listing. The reservation gives a check-in and a check-out date with no time on it. Both values therefore arrive at local midnight, and the document prints a bare date such as 2026-08-01 (UTC-04:00). Do not write a clause around a check-in hour, because the document has no hour to print.
  • The Guesty integration supplies none of the five. Its reservation carries no time zone, and the platform needs one before it can label a date. It therefore sends nothing, rather than a value that could print the wrong calendar day. A document that places any of the five variables refuses every verification that this integration starts.

To put these fields on a verification that an integration would start, create the verification from the API instead and send them yourself.

A screening rule can read session.metadata.<key>. That resolves the metadata on the workflow template, not the object you send on a create. These five keys reach document variables only. A rule condition cannot address them.