Field masks¶
A field mask restricts which fields an operation touches. Invora uses one for two jobs:
- Read masks — on Get and List, return only the listed fields (smaller payloads).
- Write masks — on Update, modify only the listed fields (partial updates).
The mask is a comma-separated string¶
Shape & casing
Over JSON, mask is a single comma-separated string of field paths in camelCase — the same names you use in the request body. It is not an object and not {"paths": [...]}, and the paths are not snake_case.
Nested paths use dots (calculations.payableAmount); list multiple paths separated by commas with no spaces required.
Read masks¶
Pass mask on a Get or List request to receive only specific fields. Unrequested fields come back zero-valued (empty string, 0, null, or omitted).
Get with a mask (skip the heavy UBL content)¶
curl -X GET "https://gateway.invora.app/api/v2/documents/doc_01J7...?mask=editState,documentType,frozenAt" \
-H "Authorization: Bearer $TOKEN"
{
"details": {
"editState": "EDIT_STATE_FROZEN",
"documentType": "DOCUMENT_TYPE_INVOICE",
"frozenAt": "2026-04-29T10:00:00Z"
}
}
This skips the full UBL content and xmlContent — ideal when you only need metadata.
List with a mask¶
curl -X POST https://gateway.invora.app/api/v2/documents/list \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pagination": { "limit": "20" },
"mask": "key,documentType,editState,branchId"
}'
Common read projections¶
| Use case | Mask |
|---|---|
| Dashboard list | key,documentType,editState,prefix,sequenceId,frozenAt,branchId |
| Status check | key,editState,regulationMetadata |
| Full document | Omit mask entirely |
Write masks¶
Pass mask on an Update request alongside concurrencyStamp. Only the listed fields are modified; everything else is left as-is.
Update only the customer party on an invoice¶
curl -X PUT https://gateway.invora.app/api/v2/documents/doc_01J7... \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"concurrencyStamp": "a1b2c3d4",
"mask": "content.invoice.accountingCustomerParty",
"changes": {
"content": {
"invoice": {
"accountingCustomerParty": {
"party": { "partyName": [{ "name": { "value": "Updated Customer Name" } }] }
}
}
}
}
}'
Settings partial update¶
curl -X PUT https://gateway.invora.app/api/v2/settings/self-party \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mask": "pdf,defaultCurrency",
"changes": { "pdf": { "showLogo": true, "showQrCode": true }, "defaultCurrency": "SAR" }
}'
Only pdf and defaultCurrency change; prefixes, regulation config, and the rest stay untouched.
Write-mask rules¶
- Omitting the mask on Update replaces the entire content — use with care.
- An empty mask (
"") is a no-op — nothing is updated. - Nested paths use dots:
content.invoice.legalMonetaryTotal.payableAmount. - Repeated fields are replaced wholesale (not merged) when their parent is in the mask.
- Map fields follow the same replace semantics.
- Paths are camelCase — the same names as the JSON body, not the proto snake_case.
Streaming with masks¶
ListStream and GetStream accept a mask too. It applies to every event in the stream and cannot change mid-stream — open a new stream to change the projection.
Related¶
- List & filtering — where read masks are most useful.
- Documents API and Simple invoicing — update flows that use write masks.
- gRPC & JSON transcoding — why paths are camelCase over JSON.
- Entity versioning —
concurrencyStampon updates.