Companies and establishments
Companies are the legal entities on whose behalf you invoice in Scribee: seller on sales invoices, buyer on purchase invoices (Issue a sales invoice). The company part of an invoice can never point at a customers and suppliers directory record: a party_id on the seller role of a sales invoice, or the buyer role of a purchase invoice, is rejected. Omit that party and Scribee derives it from the company record; send it in full and your values are the ones stored. Establishments describe a company's physical sites. Only name is required; the SIRET (legal_identifier) is optional and has no bearing on addressing in the reform directory. It is the directory entry that carries the SIRET deciding its granularity: an entry with a SIRET addresses the establishment, a SIREN-only entry covers the whole legal entity. That entry is created with its own SIRET, independently of your establishment records, whether or not those carry one (National directory). This page covers creating and managing both through the API.
What Scribee does for you
- Normalizes a company's legal identifier and VAT number at registration: leading and trailing spaces removed, letters uppercased.
- Rejects a legal identifier or VAT number already registered: each identifier exists only once in Scribee, across every workspace. An establishment's SIRET follows the same rule.
- Checks that a French company's legal identifier is its 9-digit SIREN: a SIRET is refused there.
- Checks that an establishment's SIRET has exactly 14 digits.
- Keeps at least one company per workspace: the last one cannot be deleted.
Step 1: create a company
This call creates the company record in your workspace. Nothing is transmitted externally: not to the PPF (Portail Public de Facturation), not to the Peppol network. It is undone with the DELETE from step 6, but only as long as the company has produced no attached data: step 6 lists every blocker, read it before relying on that reversibility. The write scope is required.
curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company": {
"name": "Acme Corp SAS",
"commercial_name": "Acme",
"legal_registering_country": "FR",
"legal_identifier": "443061841",
"vat_identifier": "FR32443061841"
}
}'
201 response, abridged to the fields useful here (each company also carries created_at and updated_at):
{
"data": {
"id": 7,
"name": "Acme Corp SAS",
"commercial_name": "Acme",
"legal_registering_country": "FR",
"legal_identifier": "443061841",
"vat_identifier": "FR32443061841",
"company_legal_form": null,
"parent_id": null
}
}
name is the only required field. legal_registering_country names the country that registered the company; left blank, it counts as France. When it is FR or blank, legal_identifier carries the company's SIREN, exactly 9 digits: a 14-digit SIRET, like any other value, is refused with 422 and the error under details.legal_identifier (Errors and edge cases). Spaces between the digits are allowed and removed when stored: 552 100 554 is accepted and stored as 552100554, so a SIREN already recorded is refused as a duplicate whatever its spacing. The SIRET identifies an establishment and goes in the establishment's legal_identifier (step 4). For any other country, the format is free. In every case, legal_identifier is unique and at most 50 characters long, 32 for vat_identifier. The SIREN check applies on creation, then on every change to legal_identifier or legal_registering_country: a company recorded before this check with another identifier can still be modified on its other fields. Together with legal_identifier, the country decides the registration scheme carried by the invoice party derived from this record - a SIREN under a French country gives siren, and a foreign registration gives no scheme at all (Issue a sales invoice). parent_id attaches the company to a parent company in the same workspace, making it a subsidiary; a circular attachment is rejected with a 422 status, and so is a parent company located in another workspace. A parent_id that points at no existing company, however, is not validated: the call produces a server error, not a 422. Check the identifier with step 2 before sending it.
Step 2: list and look up your companies
GET /api/v1/workspaces/{workspace_id}/companies returns the workspace's companies; the read scope is enough. The include parameter (values parent, subsidiaries, establishments, comma-separated) adds the associations to each record.
curl "https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/companies?include=establishments" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 7,
"name": "Acme Corp SAS",
"legal_identifier": "443061841",
"establishments": [
{
"id": 21,
"name": "Siège Paris",
"legal_identifier": "44306184100025",
"headquarters": true
}
]
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total_pages": 1,
"total_count": 1
}
}
The list is paginated (page, per_page - 20 by default, 100 maximum) and sortable (sort_by: name, commercial_name, legal_identifier, created_at, or updated_at; sort_order: asc or desc; default sort: name ascending). The envelope and pagination are described in API conventions. GET /api/v1/companies/{id} returns a single record, with the same include parameter.
Step 3: modify a company
This call modifies the record in your workspace, with no external transmission. It is undone by restoring the previous values through the same endpoint. The fields are the same as at creation.
curl -X PATCH https://app.scribee.tech/api/v1/companies/7 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "company": { "commercial_name": "Acme France" } }'
200 response with the updated record, in the same format as step 1.
Step 4: declare establishments
This call creates the establishment record under the company, with no external transmission. It is undone with the DELETE from step 5 as long as no organizational unit is assigned to the establishment and no directory request references it. name is the only required field; legal_identifier carries the SIRET (exactly 14 digits), and that SIRET must be free across the whole Scribee installation, not only in your workspace.
curl -X POST https://app.scribee.tech/api/v1/companies/7/establishments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"establishment": {
"name": "Siège Paris",
"legal_identifier": "44306184100025",
"headquarters": true,
"address_line_1": "123 Rue de Paris",
"postal_code": "75001",
"city": "Paris",
"country": "FR"
}
}'
{
"data": {
"id": 21,
"company_id": 7,
"name": "Siège Paris",
"legal_identifier": "44306184100025",
"headquarters": true,
"address_line_1": "123 Rue de Paris",
"postal_code": "75001",
"city": "Paris",
"country": "FR"
}
}
The head office is the establishment whose headquarters flag is true. Creating a company creates no establishment: declare the head office yourself with this flag. The flag is not exclusive; mark only one establishment as head office per company.
Step 5: list, modify, delete an establishment
GET /api/v1/companies/{company_id}/establishments returns the company's establishments - paginated and sortable (sort_by: name, legal_identifier, headquarters, created_at, or updated_at), with include=company to echo the company on each record. GET /api/v1/establishments/{id} returns a single record.
curl -X PATCH https://app.scribee.tech/api/v1/establishments/21 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "establishment": { "address_line_2": "Bâtiment A" } }'
DELETE /api/v1/establishments/{id} permanently deletes the record - 204 response with no body; the write scope is required. Deletion is refused with a 422 as long as organizational units are assigned to the establishment (organizational units are managed from the Scribee interface). It is refused with the same 422 if a directory change request references the establishment - a registration made through the national directory endpoints is enough to create that reference. Both refusals carry the code dependent_records.
Step 6: delete a company
This call permanently deletes the company record and its attached data, including its establishments and its customer and supplier records. Nothing is transmitted externally. The write scope is required.
curl -X DELETE https://app.scribee.tech/api/v1/companies/7 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
204 response with no body.
A company that has carried at least one invoice cannot be deleted through the API. That is the common case in production: treat DELETE as reserved for records created by mistake and still untouched, not as a general undo mechanism.
Here is the complete list of conditions that make deletion fail:
| Condition | Response |
|---|---|
| The company is the last one in the workspace | 422, code = operation_failed, "Impossible de supprimer la dernière entreprise de l'espace de travail" |
| The company carries at least one mandate, whatever its state | 422, code = dependent_records, "Impossible de supprimer une entreprise qui a des mandats" |
| The company has subsidiaries | 422, code = dependent_records, "Impossible de supprimer une entreprise qui a des filiales" |
| The company has direct debit requests or direct debit mandates | 422, code = dependent_records, message naming the blocking association |
| The company is referenced by an invoice, an invoice upload batch, a purchasing document (order, receipt, request, match) or a directory change request | 422, code = dependent_records, message naming the blocking association |
Three notes on that table:
- The mandate blocker is permanent.
DELETE /api/v1/mandates/{id}moves the mandate to thedeletedstate and termination moves it toterminated, but both keep the row. A company that has ever carried a mandate stays blocked, whatever cleanup you perform afterwards (Electronic invoicing mandates). - The last two rows name the blocking document. The call returns a
422with the codedependent_records, whosemessagenames the association involved -"Vous ne pouvez pas supprimer l'enregistrement parce que les factures dépendants existent"when those are invoices. Several blocking associations are listed in the samemessage. Build your retry logic on thecode, never on the text. - The only way through is to handle the documents first. Delete or reassign the invoices, purchasing documents and directory requests involved from the Scribee interface, then replay the
DELETE. If those documents must be kept, the company is not deletable: leave it in place and contact Scribee.
What happens next
- The company record feeds every invoice issued under its identity (Issue a sales invoice): when you do not send the seller party of a sales invoice, or the buyer party of a purchase invoice, Scribee builds it from the company record and its head office.
- Settings consumed by invoicing are configured per company from the Scribee interface and do not appear in the API record: the numbering pattern for invoices and quotes, and the legal notices added to generated invoices (late-payment penalties, collection fees, early-payment discount).
company_legal_form(the legal form) is present for reading in responses but is set from the interface. - Registering companies in the reform's national directory is handled through the directory endpoints, described in The national directory.
Errors and edge cases
422: missing field or invalid format
An establishment legal_identifier that is not a 14-digit SIRET:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Legal identifier doit être un SIRET valide (14 chiffres)"]
}
}
The two resources do not fill details the same way. An establishment groups its errors under the base key, each message prefixed with the humanized field name, as above. A company carries one key per faulty field - the field name as it appears in the API - and its messages have no such prefix: a company without name gives "name": ["doit être rempli(e)"]. Fix the field and replay the call.
A French company legal_identifier that is not a 9-digit SIREN - here a SIRET, which belongs on the establishment:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"legal_identifier": ["doit être le SIREN à 9 chiffres d'une entreprise française, et non un SIRET à 14 chiffres : le SIRET identifie l'un de ses établissements et se renseigne sur l'établissement"]
}
}
422: identifier already registered
A legal_identifier or vat_identifier already present in Scribee is rejected with "a déjà été pris", in whichever format its resource uses: under the legal_identifier or vat_identifier key for a company, under base and prefixed - "Legal identifier a déjà été pris" - for an establishment's SIRET.
Uniqueness covers the whole Scribee installation, not your workspace. The conflicting record may therefore belong to a workspace outside your application's scope: you will not find it, and replaying the call changes nothing. Search your own workspace first; if nothing matches, the identifier is taken elsewhere and you need to contact Scribee.
422: deletion blocked
A refused deletion returns the reason in message, with no details object:
{
"error": "unprocessable_entity",
"code": "dependent_records",
"message": "Impossible de supprimer une entreprise qui a des filiales"
}
"Impossible de supprimer une entreprise qui a des mandats": this blocker is permanent, no API call lifts it (see step 6)."Impossible de supprimer la dernière entreprise de l'espace de travail": every workspace keeps at least one company. This refusal alone carries the codeoperation_failedrather thandependent_records."Impossible de supprimer un établissement auquel des unités organisationnelles sont assignées": remove the assignments from the Scribee interface first.- Any other attached document produces a message built by Scribee that names the blocking association, on the model of
"Vous ne pouvez pas supprimer l'enregistrement parce que les factures dépendants existent".
422: unresolved reference
Two situations come down to a reference that does not resolve. Both stay inside the usual error envelope:
- A company
POSTorPATCHwhoseparent_idpoints at no existing company:codeisvalidation_failedanddetailscarries theparentkey. Check the identifier with step 2 before sending it. - A
DELETEof a company or establishment still referenced by documents (see step 6):codeisdependent_recordsand themessagenames the blocking association.
In both cases, fix the data upstream, or contact Scribee.
404 Not Found
The company or establishment does not exist, or belongs to a workspace outside your application's scope:
{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}
Check the identifier and the workspace attachment (Your first call).
On GET, PATCH and DELETE /api/v1/companies/{id} and /api/v1/establishments/{id}, an IP address missing from the workspace's allowlist produces this same 404, not a 403: the record exists, but your call does not see it. Check your calling IP before concluding the identifier is wrong.
403 Forbidden
On endpoints prefixed by {workspace_id}, an OAuth client not attached to the workspace receives "L'application n'a pas accès à cet espace de travail", and an IP address missing from the workspace's allowlist receives "Cette adresse IP n'est pas autorisée pour cet espace de travail". A token without the required scope (write to create, modify and delete) receives:
{
"error": "forbidden",
"message": "Vous n'êtes pas autorisé à effectuer cette action"
}
Request a new token with the scopes you need (Authentication).
400 Bad Request: page beyond the last
On lists, requesting a page beyond the last page of a non-empty collection returns "Le numéro de page dépasse le nombre de pages disponibles". Bound your requests with meta.total_pages.
Related pages
- The national directory - register your companies in the reform's directory and look them up
- Issue a sales invoice - the journey in which the company record becomes the seller party
- API reference: list companies
- API reference: create a company
- API reference: list a company's establishments