Tags & custom fields
Classify documents with tags and attach typed custom_fields. Filter lists by tag. How they differ from metadata.
Two cross-cutting fields let you classify and enrich documents with your
own business data: tags (free classification labels you can filter
lists by) and custom_fields (an ordered list of typed
{field, value} pairs). Both are set on create/update and returned on
every read.
| Field | Shape | Limits | Filterable | Resources |
|---|---|---|---|---|
tags | array of slugs | ≤ 30, each ≤ 40 chars | Yes (?tags=, ?tags[in]=) | invoices, quotes, proformas, delivery_notes, purchase_invoices, recurring_invoices, products |
custom_fields | ordered array of {field, value} | ≤ 50 entries | No | invoices, quotes, proformas, delivery_notes, purchase_invoices, recurring_invoices |
Tags
A tag is a lowercase slug matching [a-z0-9-] — letters, digits and
hyphens only. Each tag is at most 40 characters, and a document
carries at most 30 tags. Pass them as a JSON array of strings on
create or update:
curl -X POST https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"series_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02",
"issued_on": "2026-05-15",
"due_on": "2026-06-15",
"tags": ["consultoria", "cliente-vip"],
"lines": [
{ "description": "Monthly service", "quantity": 1, "unit_price": 99.00, "tax_rate_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03" }
]
}'Tags are returned as a plain array on every read (empty [] when there
are none):
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
"number": "F-2026-0042",
"tags": ["consultoria", "cliente-vip"]
}tags is a full replacement on update: sending "tags": ["a"]
replaces the whole set, it does not append. To add a tag, send the
full list including the existing ones. To clear them, send [].
Filtering by tag
List endpoints accept two query parameters to filter by tag — pick one:
| Parameter | Semantics | Example |
|---|---|---|
tags | Exact match on a single slug. | ?tags=cliente-vip |
tags[in] | Comma-separated list, OR semantics — matches documents carrying any of the slugs. | ?tags[in]=cliente-vip,moroso |
# All invoices tagged "cliente-vip"
curl -G https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
--data-urlencode "tags=cliente-vip"
# Invoices tagged "cliente-vip" OR "moroso"
curl -G https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
--data-urlencode "tags[in]=cliente-vip,moroso"The tag filter is available on invoices, quotes, proformas,
delivery_notes, purchase_invoices and recurring_invoices. It
combines with the other filters and with cursor
pagination.
Custom fields
custom_fields is an ordered array of typed {field, value} pairs,
for business data you want to display alongside the document (cost
centre, purchase order number, project code…). Up to 50 entries;
each field is a non-empty string of at most 60 characters and each
value is a string of at most 500 characters. Order is preserved
exactly as you send it.
curl -X POST https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"series_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02",
"issued_on": "2026-05-15",
"due_on": "2026-06-15",
"custom_fields": [
{ "field": "centro_coste", "value": "CC-2026-001" },
{ "field": "numero_pedido", "value": "PO-2026-0042" }
],
"lines": [
{ "description": "Monthly service", "quantity": 1, "unit_price": 99.00, "tax_rate_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03" }
]
}'Like tags, the whole array is a full replacement on update, and it is
returned in order on every read (empty [] when there are none).
Custom fields vs metadata
Both custom_fields and metadata carry your own data, but they serve
different purposes — don't reach for the wrong one.
Use custom_fields for ordered, typed business data the user sees
on the document. Use metadata for an unordered key→value map of
opaque integration data (ERP codes, your own ledger references) that
nobody reads visually. A document may carry both.
custom_fields | metadata | |
|---|---|---|
| Shape | Ordered list of {field, value} | Unordered map key → value |
| Order | Preserved | None |
| Limit | ≤ 50 entries | ≤ 50 keys |
| Key | field, 1–60 chars | map key |
| Value | string ≤ 500 chars | string ≤ 500 chars |
| Intent | Business fields the user sees | Opaque integration data |
| Resources | The six document resources | All resources |
The master resources (clients, suppliers) have no typed
custom_fields — use their metadata as the untyped custom-fields
store. products accept tags but no custom_fields.
Examples
import os, requests
base = 'https://api.factuarea.com/v1'
headers = {'Authorization': f"Bearer {os.environ['FACTUAREA_API_KEY']}"}
# Create an invoice with tags + custom_fields
resp = requests.post(f'{base}/invoices', headers=headers, json={
'client_id': '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01',
'series_id': '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02',
'issued_on': '2026-05-15',
'due_on': '2026-06-15',
'tags': ['consultoria', 'cliente-vip'],
'custom_fields': [
{'field': 'centro_coste', 'value': 'CC-2026-001'},
{'field': 'numero_pedido', 'value': 'PO-2026-0042'},
],
'lines': [
{'description': 'Monthly service', 'quantity': 1,
'unit_price': 99.00, 'tax_rate_id': '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03'},
],
})
resp.raise_for_status()
# List invoices tagged "cliente-vip" OR "moroso"
rows = requests.get(f'{base}/invoices', headers=headers,
params={'tags[in]': 'cliente-vip,moroso'}).json()['data']
print(len(rows), 'matching invoices')const base = 'https://api.factuarea.com/v1';
const headers = {
Authorization: `Bearer ${process.env.FACTUAREA_API_KEY}`,
'Content-Type': 'application/json',
};
// Create an invoice with tags + custom_fields
await fetch(`${base}/invoices`, {
method: 'POST',
headers,
body: JSON.stringify({
client_id: '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01',
series_id: '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02',
issued_on: '2026-05-15',
due_on: '2026-06-15',
tags: ['consultoria', 'cliente-vip'],
custom_fields: [
{ field: 'centro_coste', value: 'CC-2026-001' },
{ field: 'numero_pedido', value: 'PO-2026-0042' },
],
lines: [
{ description: 'Monthly service', quantity: 1,
unit_price: 99.0, tax_rate_id: '01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03' },
],
}),
});
// List invoices tagged "cliente-vip" OR "moroso"
const url = new URL(`${base}/invoices`);
url.searchParams.set('tags[in]', 'cliente-vip,moroso');
const { data } = await fetch(url, { headers }).then((r) => r.json());
console.log(data.length, 'matching invoices');# Create an invoice with tags + custom_fields
curl -s -X POST https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"series_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a02",
"issued_on": "2026-05-15",
"due_on": "2026-06-15",
"tags": ["consultoria", "cliente-vip"],
"custom_fields": [
{ "field": "centro_coste", "value": "CC-2026-001" },
{ "field": "numero_pedido", "value": "PO-2026-0042" }
],
"lines": [
{ "description": "Monthly service", "quantity": 1, "unit_price": 99.00, "tax_rate_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a03" }
]
}' | jq '{id, tags, custom_fields}'
# List invoices tagged "cliente-vip" OR "moroso"
curl -s -G https://api.factuarea.com/v1/invoices \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
--data-urlencode "tags[in]=cliente-vip,moroso" | jq '.data | length'