Skip to main content

Organizations & Multi-Tenancy

GoRoute is built for platforms that serve multiple businesses. Each business operates within its own organization, and every organization's data is isolated from every other one.

What is an Organization?โ€‹

An organization represents a single business entity on the platform. Each organization has:

  • Its own identifier
  • Its own Peppol participant registration(s)
  • Isolated invoice and transaction data
  • Its own webhook configuration
  • Its own API keys

How scoping actually worksโ€‹

An API key belongs to exactly one organization. Every request you make is scoped to that organization automatically โ€” there is no organization parameter to pass and no header to set.

# The key decides the organization. Nothing else to specify.
curl -X GET https://app.goroute.ai/peppol-api/api/v1/transactions \
-H "X-API-Key: YOUR_API_KEY"
One key, one organization

There is no X-Organization-Id header and no /api/v1/organizations/{id}/โ€ฆ route. A single key cannot read or write another organization's data, and cannot be pointed at a different organization at request time. A platform serving many businesses holds one key per organization โ€” see the platform pattern below.

Multi-Tenant Architectureโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Your Platform โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Customer A โ”‚ Customer B โ”‚ Customer C โ”‚
โ”‚ (key A) โ”‚ (key B) โ”‚ (key C) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚ โ”‚
โ–ผ โ–ผ โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ GoRoute API โ”‚
โ”‚ Each key resolves to exactly one organization โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚ โ”‚
โ–ผ โ–ผ โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Peppol Network โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Creating an Organizationโ€‹

Organizations are created during onboarding, not through the public API. You create one in the portal, or your account manager provisions it for you, and you receive an API key bound to it.

If you are a platform onboarding customers at volume, talk to us about programmatic provisioning โ€” it is an account-level arrangement rather than a public endpoint.

Related: Client Onboarding.

Reading your organization profileโ€‹

To see which organization a key belongs to, and the details registered against it:

curl -X GET https://app.goroute.ai/peppol-api/api/v1/settings/organization \
-H "X-API-Key: YOUR_API_KEY"

Response:

{
"id": "3617777c-0e1b-4c2a-9f55-1d2a4b6c8e90",
"name": "Acme Corporation",
"peppol_id": "0106:123456789",
"vat_number": "NL123456789B01",
"contact_name": "Accounts Payable",
"contact_email": "invoices@acme.example.com",
"phone": "+31201234567",
"address_line1": "123 Business Street",
"city": "Amsterdam",
"postal_code": "1012 AB",
"country": "NL",
"status": "active"
}

Updating your organization profileโ€‹

curl -X PATCH https://app.goroute.ai/peppol-api/api/v1/settings/organization \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact_email": "newemail@acme.example.com"
}'

Send only the fields you are changing.

Data Isolationโ€‹

Each organization's data is isolated:

Data TypeIsolation
InvoicesFully isolated
TransactionsFully isolated
ParticipantsFully isolated
WebhooksFully isolated
API KeysScoped to one organization
Rate LimitsApplied per organization
How isolation is enforced

Every query is filtered by the organization the API key resolves to. Because the organization is derived from the key rather than supplied in the request, a caller cannot widen its own scope by changing a parameter.

Organization Statusโ€‹

StatusDescription
pendingCreated, awaiting verification
activeFully operational
suspendedTemporarily disabled (e.g. billing issue)
closedPermanently closed

Platform Integration Patternโ€‹

For platforms serving multiple customers, hold one key per customer organization and select the key โ€” not a header โ€” when acting on their behalf:

import requests


class GoRouteClient:
"""One instance per customer organization."""

def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://app.goroute.ai/peppol-api"

def _headers(self):
return {"X-API-Key": self.api_key, "Content-Type": "application/json"}

def send_invoice(self, invoice_data: dict):
"""Create and send an invoice for this organization."""
response = requests.post(
f"{self.base_url}/api/v1/invoices",
headers=self._headers(),
json=invoice_data,
)
response.raise_for_status()
return response.json()

def get_transactions(self):
"""List this organization's transactions."""
response = requests.get(
f"{self.base_url}/api/v1/transactions",
headers=self._headers(),
)
response.raise_for_status()
return response.json()


# Map your internal customer to the key you hold for them.
clients = {customer_id: GoRouteClient(key) for customer_id, key in your_key_store.items()}

Store customer keys the way you would store any other credential: encrypted at rest, never in source control, and rotatable without a code change.

Webhooks per Organizationโ€‹

Webhooks are registered per organization, which means they are registered with the key for that organization:

curl -X POST https://app.goroute.ai/peppol-api/api/v1/webhooks \
-H "X-API-Key: CUSTOMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourplatform.com/webhooks/acme",
"events": ["invoice.delivered", "invoice.received"],
"description": "Acme Corporation delivery events"
}'

The signing secret is generated by GoRoute and returned in the response โ€” you do not supply it. See Webhooks for the event list and signature verification.

Best Practicesโ€‹

  1. One organization per business โ€” do not share an organization between customers
  2. One key per organization โ€” map your internal customer ID to the key you hold
  3. Validate ownership in your own layer โ€” make sure a user of your platform can only reach the key that belongs to them
  4. Separate webhook endpoints โ€” a distinct URL per customer makes delivery failures easy to attribute

Next Stepsโ€‹