API & integrations
Everything you need before the first call: authentication, limits, errors, approval lock, webhooks, SCIM, MCP.
The integration API covers nearly the full functionality of the application: AI systems, risk assessment, checklists, documentation, governance, assurance, controls, incident management, the complete GDPR suite, model registry, vendor register, legal logic, export and administration. This page explains the rules that apply across all of them. The full endpoint reference lives in your own instance at /api/docs, the getting-started guide at /api/guide.
At a glance
- Included from
- Enterprise (Custom API Extension)
- Where in the app
- Enterprise options → API
- Output
- Server-to-server access to your compliance data
- API
- https://<ihre-organisation>.simpleact.de/api/v1
The four ground rules
The key determines the organization
Every API key belongs to exactly one organization. Which data a call sees is determined solely by the key – not by the domain you call. That is why most endpoints carry no organization ID at all.
The key is shown once
At creation you see the key exactly once; afterwards it cannot be retrieved. Move it straight into your secret manager. Lost means: create a new key, revoke the old one.
One key per application
The hourly budget is counted per key. A nightly sync and a dashboard on the same key starve each other, and a rate-limit error will not tell you which of them spent the budget. Separate keys can also be revoked individually.
Permissions are granted explicitly
Every key carries its own permission list. A key can never hold more permissions than the person creating it. Administrative permissions – creating users, changing organization settings – are never part of the default set and must be granted deliberately.
Authentication
- Header X-API-Key: sk_live_… – or alternatively Authorization: Bearer sk_live_…
- The base URL is your own organization domain plus /api/v1. Examples from the in-app documentation therefore work unchanged.
- The API is designed for server-to-server calls. A key never belongs in a web page: cross-origin requests from foreign origins get no CORS clearance and are blocked by the browser. Servers, CLIs and jobs are unaffected.
- Reference and guide answer to the same credential: you can fetch the OpenAPI specification from /api/docs-json in a script, with no browser session.
Limits
Both limits apply at the same time and cumulatively. A key therefore cannot spend its hourly budget in a single burst.
| Limit | Scope | On exceeding |
|---|---|---|
| 1,000 requests per hour | per API key | HTTP 429 |
| 100 requests per minute | per IP address and route | HTTP 429 |
| 100 kB request body | per request | HTTP 413 – the request is not processed at all, there is no partial import |
No rate limit headers
Responses deliberately carry no RateLimit or Retry-After headers. A live remaining count is exactly what lets a script sit right on the ceiling. Pace against the published numbers instead, and treat a 429 as the signal to back off – not as a way to find the limit.
Error codes
Every failure comes in the same JSON envelope with status code, UTC timestamp, path and message – including on download endpoints. A failed PDF or ZIP request answers as JSON, never with the media type of the success case.
| Code | Meaning |
|---|---|
| 400 | Invalid payload or a violated business rule (see below). The errors field lists every violation with field path and a stable rule name. Branch on those, not on the message text. |
| 401 | Key missing, unknown, revoked or expired. |
| 403 | Permission missing, the module is not enabled for your organization or not in its plan, or the action is closed to API keys (see below). |
| 404 | Record does not exist – or belongs to another organization. The two are deliberately indistinguishable. |
| 409 | Conflict, e.g. an email address already in use. |
| 413 / 415 | Request body too large, or wrong content type. Send JSON. |
| 429 | Rate limit exceeded; the message names which limit was hit. |
Write semantics worth knowing
- PUT and PATCH both merge: a field you leave out keeps its value. You never have to send the complete object.
- To empty a field, send null – as long as the field may be empty. On a mandatory field, null is not a way to remove it.
- List-valued fields are replaced, not appended to. A list you send overwrites the stored one; an empty list clears it; an omitted field leaves it unchanged.
- Unknown fields in the request body are rejected rather than ignored – a typo in a field name fails the call instead of silently dropping the value.
- References to other records must resolve. An ID that does not belong to your organization fails the entire call with 404 – nothing is stored.
- Dates are ISO 8601 calendar dates. A date that does not exist (say 30 February) is rejected, not silently shifted.
Business rule errors under 400
Not every 400 is a typo. When the app refuses a call because a workflow does not allow it (a status change is not permitted, a gate is closed, evidence is missing), the same envelope comes back with error: "Business Rule". The errors field then holds one entry with constraints: [rule id], field where applicable (for example status) and messages with the readable text.
Branch on the rule id, not on the text. The OpenAPI specification publishes the full list; examples are rule.approval_required, rule.annex_iv_transition, rule.checklist_justification_required, rule.governance_workflow_not_approved, rule.incident_status_transition and ai_intake.unreviewed_sections.
Approval lock: rule.approval_required
Once your organisation has set up a multi-stage approval workflow for an object type, the API can no longer set the final status directly:
- A change into the final status (for example an Annex IV dossier to APPROVED or a vendor to FREIGEGEBEN) needs a completed approval that has not been used yet. Without one, the API answers 400 with rule.approval_required; the message names the object type and the number of stages.
- A record that is to be created directly in its final status is refused for the same reason. Create it below that status and have it approved.
- Each approval is consumed exactly once. A new completion needs a new approval.
Login only, not via API key
Some actions are personal decisions. An API key acts in the name of the person who created it: exactly what the four-eyes principle is meant to prevent.
| Area | Behaviour with an API key |
|---|---|
| Approvals (/api/v1/approvals): request, decide, configure workflows | Signed-in session only; not in the OpenAPI reference. |
| Review campaigns (/api/v1/review-campaigns) | Signed-in session only; not in the OpenAPI reference. |
| AI intake: accept or reject a request | 403 (ai_intake.human_review_required). Submitting and reading requests works with an API key. |
Paging: not uniform
The API has no uniform paging. Most lists return everything at once; a few endpoints limit:
- No paging, full list: for example AI systems (newest first) and webhooks.
- page and limit: consent receipts.
- limit and offset: system log.
- limit only: GDPR audit trail on the dashboard (default 100, at most 500).
- When in doubt, check the endpoint description in the OpenAPI reference and plan for full lists in large organisations.
Language of error messages
Message texts follow the Accept-Language header (German, English, Spanish, French); without the header the API answers in English. Some technical messages are not translated and always appear in English. Rule ids and field names are language-independent. Branch on those.
Webhooks
- Delivered by POST with a JSON body { id, type, timestamp, data } and the headers X-SimpleAct-Event (event type) and X-SimpleAct-Signature.
- The signature is an HMAC-SHA256 over the body, hex-encoded, keyed with the webhook secret. Verify it against the unmodified body.
- Without your own secret, SimpleAct generates one with 64 characters; your own must have at least 16. Store it safely at creation. Display of the secret is being limited to the moment of creation.
- No automatic retry: a failed delivery is not repeated. After ten consecutive failures the webhook is deactivated and cannot be re-enabled through the API.
- Targets in private or local networks are blocked. A registration cannot be changed; for a new URL or new events, delete and create it again.
- Event catalogue: currently there is only the test delivery webhook.test, used to check your receiving end. Business events are added step by step; only types from the catalogue can be subscribed to.
SCIM and MCP
SCIM 2.0
User provisioning from Entra ID or Okta: create, update, deactivate and query users, plus the discovery endpoints. Groups are not supported. SCIM uses an API key with the provisioning permission as a bearer token.
MCP interface
At /api/v1/mcp SimpleAct speaks the Model Context Protocol for AI assistants: nine tools, eight read-only and one that writes (ai_intake_submit, files a request in the AI intake). API key only; every tool needs the same permissions as the matching REST endpoint.
What the API deliberately does not cover
These areas are not missing, they are excluded – each for a concrete reason.
- Billing and subscriptions: payment data is only changed through the audited payment flow, so no integration bug can cause incorrect charges.
- API key management itself: keys cannot create or extend one another. Creating a key stays a deliberate act by a signed-in administrator.
- Session-bound flows – sign-in, SSO, password reset, personal profile, switching organizations. They concern the signed-in human, for which a long-lived key is no sensible counterpart.
- Platform-wide super admin functions. They are unreachable for API keys.
Treat administrative keys like an admin password
User management permissions turn a key into administrator access. Grant them only if your integration genuinely has to manage users, scope the key narrowly to exactly the permissions needed – no catch-all key – and rotate it regularly. Revoke it immediately when the integration is replaced or the responsible person leaves your company.