Skip to content

Partner overview — connecting a new system

Audience: partners & internalStatus: reference

This is the onboarding narrative for someone who has been asked to connect an external system to esz-backend and knows nothing about it yet. It threads together the reference pages in the order the work happens; each section points to the page that holds the canonical detail.

No prior knowledge of the codebase is assumed. Nothing here is code you have to write — it describes the shape of the work and the decisions you own.


1. What you are connecting to

esz-backend is ESZ's internal backend: a set of independent services, each owning one business domain and its own database, holding the company's canonical data and keeping it in sync with the systems the business runs on (Personio, Büro+, Business Central, Salesforce, Hivebuy, Microsoft 365).

The full service catalogue and the three design rules — service independence, external systems never owning our data, and the hub rule — are in Platform and services. Read that first; the rest of this page assumes it.

2. Where your system fits

Your system is added as a vendor app inside the service that owns its data — employee data into employees, sellable catalogue data into products, purchasing catalogue data into procurements, and so on — never as a new service. The vendor app is an anti-corruption layer: your ids and field names stop at a mirror, which is translated into our canonical model, and your shape never leaks past that boundary.

The diagram and the two claims it makes are in Platform and services → Where a new external system fits. The design reasoning is in Domain modelling.

3. How the two sides authenticate

Your integration is machine-to-machine throughout. There are four distinct auth hops — you calling us (client credentials followed by Bearer), your webhooks to us (signed payload, no token), and us calling you (client credentials) — and confusing them is the most common source of wasted meetings.

The full table, the signature-verification rules, and rotation are in Authentication.

4. What the endpoints look like

Every vendor app mounts at /v1/<vendor-name>/. We pull from you (sync trigger), you push to us (webhook, 202), we push to you (scheduled sync), and — where you need on-demand reads — you read an agreed published field list from us.

The request/response shapes, the published-field contract, and the OpenAPI locations are in Endpoints and contracts. The retry and idempotency rules the status codes imply are in Errors and retries. Hostnames, sandbox requirements, and egress allowlisting are in Environments.


5. What we need from you

This is the checklist. Answering it is the main work before engineering starts.

5.1 Identity and access

  • API base URL for production and for a test/sandbox environment. A sandbox is not optional — we do not develop against your production data.
  • Auth for us → you: OAuth2 client-credentials token URL, or a static API key. Named contact for issuance and rotation. (Hop 4 in Authentication.)
  • Auth for you → us, if you emit webhooks: how do you sign? Which headers? Who generates the shared secret? (Hop 3 in Authentication.)
  • Rate limits and what happens when we hit them (429 with Retry-After? a hard block?). See Environments.
  • IP allowlisting — if required, tell us early; our egress addresses need to be registered before anything works.

5.2 The data contract

  • Which entities cross the boundary, in which direction, for each entity.
  • The stable identifier for each entity on your side. Not a name, not an e-mail — an id that never changes for the life of the record. Our mirror is keyed on it, and changing it later is a migration, not a config edit.
  • Field-level schema: types, required/optional, allowed values for enumerations, units and currency for numbers, timezone for timestamps.
  • Field authority. For every field that exists on both sides: who wins on conflict? This is a business decision, not a technical one, and it must be answered field by field.
  • Why each field. For every field you want from us: what does your system do with it? Fields without an answer are not published (Endpoints and contracts).
  • Deletion semantics. Hard delete, soft delete, or archive? How do we learn a record is gone?
  • Personal data. Which fields are personal data under GDPR, in either direction. A data processing agreement must be in place before production cutover — see Data protection.

The full method for answering the data-contract questions — the six questions, per entity, with a worked example — is in Domain modelling.

5.3 Behaviour

  • Change detection: can you give us "everything modified since timestamp X"? If not, we full-scan, and volume becomes a cost question.
  • Idempotency on your write endpoints: supported? via which header? See Errors and retries.
  • Expected volume: records at initial load, and daily delta.
  • Support channel and escalation path for production incidents.

5.4 Documentation

  • OpenAPI / GraphQL schema, or the closest thing that exists. If it does not exist, we will reverse-engineer from your sandbox — slower, and it produces a more fragile integration. Say so early.

6. What we bring

So you know where the boundary of responsibility lies:

  • A mirrored copy of your data inside the owning service, with an admin UI to inspect it, see per-record sync errors, and re-trigger a sync for one row.
  • Every call to your API logged centrally — request, response, latency, failure — so "did you call us?" is answerable without asking you.
  • Retry with backoff on transient failures; a visible, permanent error state on 4xx, surfaced per record rather than silently dropped.
  • An OpenAPI spec for every endpoint we expose to you.
  • A staging environment on our side, so you can test webhook delivery and read endpoints against something that is not production.
  • Tests at the boundary against a recorded fixture of your API, so a breaking change in your schema breaks our build rather than our production data.

7. Rough shape of the work

Ordering matters more than day counts, which vary with your API's quality.

  1. Discovery — the §5 checklist gets answered. Sandbox credentials issued both directions. Ends with a written data contract: entities, direction, identifiers, field authority — and, where you read from us, the exact list of published fields.
  2. Mirror — we model your entities inside the vendor app and get a read-only sync running against your sandbox. Nothing writes to our canonical data yet.
  3. Upsert — the vendor app writes into the canonical model. Conflict rules from step 1 are implemented and tested. This is where field-authority decisions become real, and where they get relitigated if they were vague.
  4. Outbound (only if your system consumes ESZ changes) — scheduled delivery or published read endpoint, idempotency, retry behaviour agreed.
  5. Production cutover — production credentials, initial full load, reconciliation, monitoring in place.

The step that reliably slips is step 1, and always on field authority. "Who wins when Personio and Büro+ both hold a phone number?" is not a question engineering can answer alone.


For anything not covered here, the owning service's team is the contact — which service owns your integration follows from which entity your system carries (Platform and services).