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"
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 Type | Isolation |
|---|---|
| Invoices | Fully isolated |
| Transactions | Fully isolated |
| Participants | Fully isolated |
| Webhooks | Fully isolated |
| API Keys | Scoped to one organization |
| Rate Limits | Applied per organization |
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โ
| Status | Description |
|---|---|
pending | Created, awaiting verification |
active | Fully operational |
suspended | Temporarily disabled (e.g. billing issue) |
closed | Permanently 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โ
- One organization per business โ do not share an organization between customers
- One key per organization โ map your internal customer ID to the key you hold
- Validate ownership in your own layer โ make sure a user of your platform can only reach the key that belongs to them
- Separate webhook endpoints โ a distinct URL per customer makes delivery failures easy to attribute