Common Patterns
Production-ready patterns for protecting routes, org switching, token refresh, rate limits, idempotency, and API versioning
These patterns cover the most common integration tasks beyond the quickstart. They apply across frameworks and complement the SDK reference and API reference.
Protecting API routes
Validate the caller's access token on every protected backend route. Do not trust client-side checks alone — the Protect component and similar UI guards are for rendering only.
Next.js (App Router)
Use the @wocha/nextjs middleware and server helpers:
Express
Use the Express SDK JWT middleware:
FastAPI
Validate JWTs with your issuer JWKS or introspection endpoint:
Role-based UI rendering
Use the Protect component or permission hooks to show UI only when the signed-in user has the required role or permission. Keep sensitive actions enforced on the server as well.
SpiceDB-backed permissions from the Management API (permissions.check, permissions.lookup) are the source of truth for backend authorisation.
Organisation switching
When a user belongs to multiple organisations, issue a new token with the selected org_id claim. The Account API and SDK org switcher handle the consent flow.
Client-side (React):
Server-side (session switch hint):
Prefer the Customer API (/v1/users, /v1/organisations) for management operations; legacy M2M routes under /api/v1/identities and /api/v1/organisations are deprecated.
Token refresh patterns
Access tokens expire. Use refresh tokens (via the offline_access scope) to obtain new access tokens without re-prompting the user.
Next.js — automatic refresh in server helpers:
Manual refresh (any framework):
Store refresh tokens securely (HTTP-only cookies or encrypted server-side storage). Never expose them to browser JavaScript unless using a confidential backend-for-frontend pattern.
Handling rate limits
The Customer API returns rate limit headers on every authenticated response:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Maximum requests in the current window |
X-RateLimit-Remaining | Requests remaining |
X-RateLimit-Reset | Unix timestamp when the window resets |
On 429 Too Many Requests, the response includes retry_after (seconds) in the JSON body and a Retry-After header.
Implement exponential backoff for background jobs. Avoid tight retry loops that amplify load during outages.
Idempotency keys
Mutation endpoints on the Customer API support the Idempotency-Key header. Send a unique key (UUID v4 recommended) per logical operation so safe retries do not create duplicate resources.
The API caches the response for 24 hours. Replaying the same key with the same payload returns the original response; replaying with a different payload returns 409 Conflict.
Use idempotency keys for:
- User and organisation creation
- Webhook registration
- API key rotation
- Any client that retries on network failure
API versioning
Wocha uses date-based API versions (similar to Stripe). Pin behaviour with the Wocha-Version header:
| Mechanism | Purpose |
|---|---|
URL path /v1/ | API surface version (Customer API vs Account API) |
Wocha-Version header | Date-based behaviour version within that surface |
When the header is omitted, the latest version (2026-06-01) applies.
Changelog (2026-06-01)
Breaking changes in the current version:
- Unified error format:
{ error, message, request_id, docs_url } - Legacy M2M routes deprecated (see migration guide)
Legacy error format
To receive the older { error, code } shape during migration, either:
- Pin
Wocha-Versionto a date before2026-06-01, or - Send
Accept: application/vnd.wocha.v0+json
Deprecated routes
Legacy M2M routes return Deprecation, Sunset, and Link headers:
| Legacy route | Replacement |
|---|---|
/api/v1/identities/* | /api/v1/customer/users/* |
/api/v1/organisations/* | /api/v1/customer/organisations/* |
/api/v1/products/* | /api/v1/customer/products/* |
/api/v1/webhooks/* | /api/v1/customer/webhooks/* |
Sunset date: 12 December 2026. Migrate before then to avoid disruption.
See the error reference for standard error codes and handling patterns.