1. Technical prerequisites
1.1 - Create Tomorro superadmin account
automation@customer.com), not a personal account.- The API key is bound to a member, not to the organization. Every write performed through the API is attributed to that member and inherits its permissions.
- Consequence: the counterparty history shows this account as the author of every creation and update made by the integration. A named account such as “ERP automation” keeps it readable.
- Recommended: no personal mailbox behind it, and exclude it from Tomorro notification settings.
1.2 - API key
x-api-key header: Step 1: Get your API key1.3 - Counterparty smart fields in Tomorro
- Libraries > Dynamic components > Smart fields > New, with Data related to = Counterparty smart field.
- Pick the type matching the ERP: Text for SIRET, address and city, Single-choice selector for the industry (options = the ERP values, see 3.5).
- Copy each smart field id from the URL of the field (last segment) next to your mapping (Non-Technical scoping):
1.4 - ERP: add the ID tomorro column
ID tomorro to the counterparty table, empty for every counterparty. The integration fills it; users must not edit it. If the ERP supports it, make it read-only for users.2. Flows
Flow 0 - [ERP → Tomorro → ERP] Initialisation
Step 0 - Triggered by hand
ID tomorro is empty (and that matches your scope, see Non-Technical scoping). Re-running it is safe: linked counterparties are skipped.Step 1 - Get the picklist option ids
200:Notes
Notes
- Build a
label → idtable fromdata[].translations[].valueanddata[].id. Match labels case-insensitively. - Up to 50 options per page: follow
pagination.next_cursorwithafterif there are more. integrationMappingscan hold your ERP codes for each option, to translate on codes instead of labels.
Step 2 - Create the counterparty in Tomorro, one call per ERP row
201:Notes
Notes
nameis the only required field.statusdefaults toactive.fieldskeys are smart field ids (1.3). Text values as strings, picklists as the option id (step 1, 3.5). Leave a field out rather than sending an empty string.- The body is strict: an unknown top-level key is refused with a
400. An unknown smart field id is refused too. - Read the new id under
data.id. In the response, each field comes back as{ type, value }, one entry per counterparty smart field of the workspace, filled or not. - Duplicates: Tomorro does not deduplicate counterparties. Calling this endpoint twice creates two counterparties. This is why Flows 0 and 1 only take counterparties with an empty
ID tomorro.
Step 3 - Write the Tomorro id back to the ERP
data.id from step 2 into the ID tomorro column, straight away: until it is written, a new run would create the counterparty a second time.Flow 1 - [ERP → Tomorro → ERP] Counterparty created in the ERP
Step 0 - Triggered by the ERP
ID tomorro is already filled or its name is empty.Steps 1 to 3 - Same as Flow 0
data.id back to the ERP.Flow 2 - [ERP → Tomorro] Counterparty updated in the ERP
Step 0 - Triggered by the ERP
ID tomorro go further.Step 1 - Read the current counterparty and the option ids
200: same shape as the creation response. Read data.name and data.fields["<smart field id>"].value. Get the option ids as in Flow 0 step 1.Step 2 - Compare, then update only what changed
200: the updated counterparty.Notes
Notes
- Only the keys you send change. Send
nameonly if it changed, and only the smart fields that changed. nullclears a smart field (the value was emptied in the ERP).404: the counterparty no longer exists in Tomorro (deleted, or wrongID tomorro). Apply the rule chosen in 3.6.- To archive a counterparty deactivated in the ERP, send
{ "status": "archived" };{ "status": "active" }restores it.
Flow 3 - [Tomorro → ERP] Counterparty updated in Tomorro
Step 0 - Scheduled, every day at 8:00
updatedAt you processed, saved at the end of the previous run. On the first run, look back 24 hours.Step 1 - List the counterparties updated since the last run
updatedAt is after the saved value, then save the newest updatedAt for the next run. Get the option ids once (Flow 0 step 1).Notes
Notes
updatedAtmoves on every change of the counterparty, smart fields included.- If all 50 counterparties of the page changed since the last run, more are waiting: follow
pagination.next_cursorwithafter. - Counterparties changed by Flow 2 come back here too: the comparison of step 2 finds nothing to write (3.3).
Step 2 - Update the ERP with the values that differ
ID tomorro equals that counterparty’s id. No row: the counterparty exists only in Tomorro, ignore it (see Non-Technical scoping). Otherwise translate picklist option ids back to labels, compare with the ERP and write only the differences. No difference, no write: an unnecessary write would fire Flow 2 again.3. Technical references
Reference material: conventions, the counterparty object and error handling. Read it once, then come back to it while implementing the flows above.3.1 - API conventions
- REST only. The whole integration runs on the public REST API.
- Base URL:
https://api.tomorro.com/v2 - Authentication:
x-api-key: <your api key>on every call. A missing or unknown key returns 401. - Versioning: optional
tomorro-versionheader, defaults to the latest version. - Rate limit: a
429means too many calls in a short time. Retry with backoff. - Sandbox: available on request. It is an empty sandbox, not a copy of your production workspace.
- Content type:
application/json - Response envelope: every successful JSON response is wrapped in a
dataobject (errors come as{ error, meta }, see 3.4). Readdata.id,data.fieldsand so on. - Full reference: Counterparties - Tomorro API
3.2 - Counterparty endpoints and fields
3.3 - Loops and conflicts
Flows 1 and 3 write into the ERP (so Flow 2 sees an updated counterparty), Flow 2 writes into Tomorro (so Flow 3 reads a different counterparty). Without care, a change bounces forever.- Compare before writing, in both directions. Flow 2 only sends a
PATCHwhen an ERP value differs from Tomorro; Flow 3 only writes the ERP when a Tomorro value differs. The echo of a write finds identical values and stops. - Compare normalised values: trim spaces, treat empty and
nullthe same, compare picklists on the option id. - Same-day conflict: if a field is changed in both systems between two runs of Flow 3, the ERP value wins, since Flow 2 runs first.
3.4 - Error handling
Every endpoint follows the standard error format of the REST API. Error envelopeerror.statusCodeis a string, not a number.meta.requestIdis the value to quote to Tomorro support when reporting a failing call. Log it.
3.5 - Picklists
- A picklist value is stored as the option id, not its label. Send
"Logistics"and the API accepts it, but Tomorro shows an empty field. - Get the ids with
GET /v2/smart-fields/b25c969a-2a1c-4903-be86-10269aeb5e84/optionsand translate label ↔ id in the integration, in both directions. - Align the labels on both sides first (here,
Software & Saasin Tomorro renamedSoftware & SaaS). A label missing from Tomorro is left empty, never invented.
3.6 - Edge cases
- Link: an
ID tomorrocolumn in the ERP. Never match on name or SIRET. - Existing counterparties before Flow 0: clean them up, or link them by filling
ID tomorroby hand (Flow 0 skips linked rows). - Counterparty deactivated in the ERP: archive the counterparty (
status: archived) or ignore. - Counterparty deleted in Tomorro: Flows 2 and 3 get a
404. Alert, or clearID tomorro. - Loops: every update flow compares before writing, so the echo of a write stops by itself (3.3).
- Triggers: prefer an ERP webhook or event, so Flows 1 and 2 run as soon as a counterparty changes. Without one, poll the counterparties changed since the last run, e.g. every 15 minutes.