Metadata
metadata is a free-form key-value store on an application. Use it to carry your own identifiers (your internal customer id, the plan a customer is on, which of your systems created the record) so you can correlate Steward data with your own without maintaining a separate mapping table.
Steward never interprets metadata. It is stored and returned verbatim, and nothing in scoring, screening, or workflow behaviour reads it.
It is also not included in exports. An exported application carries the record itself, not your annotations on it. If you need your own identifiers to travel with an export, carry them from the application read instead of expecting them in the file.
Warning: Because it is returned on every read of the application, do not put anything sensitive in metadata: no personal data beyond an opaque identifier, no secrets, no card or account numbers.
Setting metadata
Section titled “Setting metadata”Send metadata when creating an application, or PATCH it later:
curl -X PATCH https://api.getsteward.ai/api/v1/applications/{id} \ -H "Authorization: Bearer stw_live_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "externalRef": "abc-123", "plan": "enterprise" } }'Merging, not replacing
Section titled “Merging, not replacing”This is the part that most often surprises people. A PATCH merges into the existing metadata rather than replacing it. Keys you do not mention are left alone.
Starting from { "externalRef": "abc-123", "plan": "enterprise" }:
| You send | Result | Why |
|---|---|---|
{"plan": "starter"} |
{"externalRef": "abc-123", "plan": "starter"} |
plan updated, externalRef untouched |
{"region": "eu"} |
all three keys | New key added to the existing set |
{"plan": null} |
{"externalRef": "abc-123"} |
A null value deletes that one key |
{} |
no metadata at all | An empty object clears everything |
metadata omitted entirely |
unchanged | Absent means “don’t touch metadata” |
{"metadata": null} |
400 |
Rejected: send {} if you mean to clear |
Two of these are easy to get wrong:
- To remove a single key, set it to
null: not"". An empty string is a valid value and stores an empty string. {}clears everything. If you build a patch object programmatically and it happens to come out empty, you will wipe the metadata rather than no-op. Omit the field entirely when you have nothing to change.
Because there is no replace mode, “set metadata to exactly this” is two steps if you need it: send {} to clear, then send the new set.
Limits
Section titled “Limits”| Rule | Limit |
|---|---|
| Number of keys | 50 (counted after the merge is applied) |
| Key length | 1–40 characters |
| Key characters | Letters, digits, hyphens, and underscores only |
| Value type | String (numbers, booleans, objects, and arrays are rejected) |
| Value length | Up to 500 characters |
The key limit is enforced against the merged result, so a patch that is small on its own can still fail if it would push the total past 50. Both the shape rules and the key-count check return 400 with a message naming the offending key.
Values must be strings. Serialise anything structured yourself:
{ "metadata": { "orderCount": "42", "flags": "{\"beta\":true}" } }Reading it back
Section titled “Reading it back”metadata comes back on the application from both GET /applications and GET /applications/{id}. An application with no metadata returns null rather than {}, so guard before indexing into it.
Metadata changes are recorded in the audit trail, which reports which keys were added, modified, or removed. The values themselves are not written into the audit entry.