Authentication
How to authenticate with the planni Public API using API keys.
Base URL
All API requests use the following base URL:
https://api.planni.de/v1API Keys
Every request must include an X-Api-Key header with a valid API key.
curl -H "X-Api-Key: your-api-key" https://api.planni.de/v1/contactsAPI keys are created in the planni admin under Settings > Integrations.
Scopes
Each API key can be assigned one or more scopes to restrict access:
| Scope | Description |
|---|---|
contacts:read | Read contacts |
contacts:write | Create and update contacts |
orders:read | Read orders |
events:read | Read event registrations |
webhooks:manage | Manage webhook subscriptions |
An API key created without scopes has full access. A key cannot be created with an empty scope list: the request is refused, so choosing no permissions never yields a full-access key.
Pagination
List endpoints support pagination with limit and offset query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Max items to return (min 1) |
offset | integer | 0 | Items to skip |
curl -H "X-Api-Key: your-api-key" \
"https://api.planni.de/v1/contacts?limit=10&offset=20"Record IDs
Every record carries an id. Its shape depends on the account:
| Record | Today | After the account moves to UUID keys |
|---|---|---|
| Contacts, events, … | integer, e.g. 42 | UUID string, e.g. "0192f5a1-7b3c-7d2e-8f00-123456789abc" |
| Orders | UUID string | UUID string (unchanged) |
planni moves each account to UUID keys once. From then on responses return the UUID string in id.
Both shapes are accepted on input. An integer id you stored before the move keeps working afterwards and serves the same record as its UUID, so PUT /v1/contacts/42 and PUT /v1/contacts/0192f5a1-7b3c-7d2e-8f00-123456789abc update the same contact. Order ids have always been UUIDs: an integer order id answers 404.
An id that names no record, or that is neither a plain integer nor a UUID (for example 007, +42 or 42.0), answers 404 Not Found. In PUT /v1/contacts/{id} and GET /v1/orders/{id}, an id longer than 64 characters is refused with 400 Bad Request, except an order id made only of digits, which answers 404.
Recommendations:
- Store ids as strings and compare them as strings, so both shapes fit.
- Do not parse or do arithmetic with ids.
- A tool that deduplicates on
id(such as a Zapier trigger) sees each existing record under its new id once after the move. - Ids copied into an order when it was placed (for example the product of an order item) keep the value they had then.
Error Handling
The API returns standard HTTP status codes.
Error Responses
{
"error": "Unauthorized",
"message": "Missing or invalid API key"
}Status Codes
| Status | Meaning |
|---|---|
| 400 | Bad request (missing/invalid parameters) |
| 401 | Missing or invalid API key |
| 403 | Insufficient scope for this endpoint |
| 404 | Resource not found |
| 500 | Internal server error |
Rate Limiting
There are currently no rate limits enforced. This may change in future versions.