Custom fields
Define your own labels for a company's invoices (store, site, cost centre), fill them in, and filter and export by them.
A custom field is a label that belongs to one company: Store, Site, Cost centre, Project. You define it once, and every invoice of that company can carry a value for it. People see it on screen and, if you choose, on the PDF; you can filter the invoice list and the spreadsheet export by it.
BeeL. gives a field no meaning of its own. It never affects taxes, totals or numbering.
| What | Operation |
|---|---|
| Create or update a field | PUT …/custom-fields/{key} |
| List the fields, with their options | GET …/custom-fields |
| Get one field | GET …/custom-fields/{key} |
| List the options of a field | GET …/custom-fields/{key}/options |
| Add an option in advance | POST …/custom-fields/{key}/options |
| Rename, archive or reactivate an option | PATCH …/custom-fields/{key}/options/{option_id} |
All paths hang off /v1/companies/{company_id}. Reading needs the custom-fields:read scope and writing needs custom-fields:write. Test and Live keep separate catalogues: a field created with a Test key does not exist in Live.
Metadata or custom fields
Both attach extra data to an invoice. Pick by who reads it:
metadata | custom_fields | |
|---|---|---|
| For | Technical identifiers of your integration: order id, tenant, source system | What a person sees, filters or exports: store, site, cost centre |
| Visible to people | No | Yes, on screen and optionally on the PDF |
| Defined beforehand | No, free key/value pairs | Yes, a catalogue per company |
| Filter | Exact match on any key, see Filtering by metadata | Exact match on the text saved on the invoice, which the options keep consistent |
If a person will ever read the value, use a custom field. If only your code does, use metadata.
Create a field
The key is the path and it names the field for good: lowercase letters, digits and _, up to 64 characters. PUT creates the field or updates it, so you can run it on every deployment to seed the catalogue. It answers 201 when it creates the field and 200 when it already existed.
curl -X PUT "https://app.beel.es/api/v1/companies/{company_id}/custom-fields/store" \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "label": "Store", "show_on_pdf": true }'label and show_on_pdf are always replaced. suggest_values and is_archived keep their current value when you omit them, so repeating the request never undoes a change someone made in the app. A new field starts active and with suggest_values: true.
Use it on an invoice
Send custom_fields as a list of key and value, at most one value per key:
{
"type": "STANDARD",
"recipient": { "customer_id": "4f244735-980b-4d9c-80e8-6331fa0b1958" },
"lines": [
{ "description": "Consulting", "quantity": 1, "unit_price": 500,
"main_tax": { "type": "IVA", "percentage": 21, "regime_key": "01" } }
],
"custom_fields": [
{ "key": "store", "value": "Gran Vía 12" }
]
}The invoice answers with the same list, and each entry also carries the label the field had when the invoice was saved:
"custom_fields": [
{ "key": "store", "label": "Store", "value": "Gran Vía 12" }
]The invoice keeps a copy of the label and the text. Renaming or archiving the field, or its options, later does not change any invoice already saved, and neither does it change the PDF when it is regenerated. An invoice with no custom fields answers [].
To change them, send custom_fields in PATCH …/invoices/{invoice_id}: the list replaces the current one as a whole, omitting it keeps the current ones, and null or [] removes them all. Only a draft, a scheduled invoice or an active proforma can change them.
Options fill themselves in
With suggest_values: true, the values already used become the options the app offers next time, per customer. When you save an invoice for a registered customer (recipient.customer_id) with a value that is new, BeeL. adds it as an option of that customer. With recipient data sent inline there is no customer to attach it to, so the text is stored and no option is created.
Before adding it, BeeL. checks whether an equivalent option already exists, among those of that customer and the common ones. Two values are equivalent when they match ignoring case, accents and repeated spaces, and ñ counts as n:
| You send | Existing option | The invoice stores |
|---|---|---|
gran via 12 | Gran Vía 12 | Gran Vía 12 |
GRAN VÍA 12 | Gran Vía 12 | Gran Vía 12 |
Peña | Pena | Pena |
Gran Vía 14 | none equivalent | Gran Vía 14, and a new option |
So a typo in case or accents does not split your filter in two. An archived option that matches is reused with its text and stays archived.
To seed a catalogue before the first invoice, add options with POST …/custom-fields/{key}/options, for one customer or, without customer_id, common to all of them. It answers 201 when it creates the option and 200 with the existing one when an equivalent is already there.
Values that change on every invoice
For something unique per invoice, such as an order number, send "suggest_values": false. The invoice then stores the text as sent, without leading and trailing spaces, it is not compared with any option and it creates none. Turning the setting off deletes no option, and turning it on again offers them again.
Read the options for a customer
GET …/custom-fields returns each field with its common options. Add customer_id to get those plus the ones of that customer, which is what an invoice for that customer can offer. A customer_id that is not a customer of the company is not an error: only the common options come back. To page through every option of a field, use GET …/custom-fields/{key}/options.
Renaming an option changes only what is offered from then on. If another option in the same scope is equivalent to the new text, the rename answers 409 CUSTOM_FIELD_OPTION_DUPLICATE.
Archive, never delete
Fields and options are never deleted, and a key never changes. To stop using a field, archive it:
curl -X PUT "https://app.beel.es/api/v1/companies/{company_id}/custom-fields/store" \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "label": "Store", "show_on_pdf": true, "is_archived": true }'An archived field cannot be added to new invoices, and invoices that already carry it keep it. Archived fields and options are left out of the listings unless you send include_archived=true.
Filter the list and export
GET …/invoices takes custom_fields[<key>]=<value>, the same bracket style as metadata. Several keys combine with AND, and also with the rest of the filters, such as customer_id. You can send up to 20 keys.
curl -g "https://app.beel.es/api/v1/companies/{company_id}/invoices?customer_id={customer_id}&custom_fields[store]=Gran%20V%C3%ADa%2012" \
-H "Authorization: Bearer $BEEL_API_KEY"The value is compared with the text saved on the invoice, exactly, case and accents included. Use the text the invoice carries, which is the one the option has. After you rename an option, invoices saved earlier are found by the old text. A key the company does not have matches no invoice.
The spreadsheet export takes the same filter inside filters:
{
"filters": {
"customer_id": "550e8400-e29b-41d4-a716-446655440010",
"custom_fields": { "store": "Gran Vía 12" }
}
}Send it to POST …/invoices/exports. It behaves like the list filter. More than 20 keys is rejected with 422 CUSTOM_FIELD_FILTER_TOO_MANY_KEYS.
Recurring templates, corrective invoices and other copies
- Recurring invoices. A template carries
custom_fieldsand copies them to every invoice it generates. If you create a template from an invoice, it takes that invoice's fields. A field archived later is left out of the invoices generated from then on, and the template keeps generating. - Corrective invoices. A corrective carries the fields of the original, each with the label the field has now. A field archived since is left out.
- Proforma to invoice. The new invoice copies the fields of the proforma, with the same rule.
On the PDF
With show_on_pdf: true, the PDF prints the field in a block of its own under the notes, with its label and value. An invoice keeps the setting it was saved with, so changing show_on_pdf affects invoices saved from then on.
Not a tax mention
Custom fields are informational. They are not part of the VeriFactu record sent to the AEAT and do not enter its hash. The mentions the regulations require (an exemption, the reverse charge) stay in their own fields of the invoice and cannot be written as a custom field.
Gotchas
| Case | Error | Status | Why |
|---|---|---|---|
A key in custom_fields is not a field of this company in this environment. Create it with PUT …/custom-fields/{key} or fix the key; nothing is created | CUSTOM_FIELD_UNKNOWN | 422 | A key in custom_fields is not a custom field of this company in this environment. |
A key in custom_fields names an archived field. Reactivate it with "is_archived": false, or send the list without it; nothing changes | CUSTOM_FIELD_ARCHIVED | 409 | A key in custom_fields names a field the company has archived. |
- The same rule applies to the create and the update of invoices and recurring templates, even when a draft already carries a field that was archived afterwards.
- Changing only the recipient of an invoice, with
custom_fieldsomitted, carries the saved fields to the new customer. A field archived since is left out with no error. - A value is text of 1 to 255 characters once leading and trailing spaces are removed; an empty one answers
422. - A repeated
keyin the same list answers422VALIDATION_ERROR.