Base & structure
- Canonical:
https://api.softcrum.com/v1/{module}/{resource}(no/apisegment). Modules:core,loyalty,messaging,crm(suite modules join as they expose public APIs). - NO Segment-style aliases in v1 (
/v1/trackdoes not exist; the canonical is/v1/core/track). A Segment-compatibility layer is a future explicit feature only if a real integration demands it. - Two API classes inside the same convention:
- Runtime (hot path, called by tenant systems in-line): track/identify/batch, redemptions, coupon validate, qualifications, members/me. SLO p95: track <100 ms (fast-ack 202), member reads <150 ms, sync decisions <300 ms.
- Management (console/integrations CRUD): everything else. SLO p95 <1 s. Pagination mandatory (cursor-based).
- SLO budgets are Definition-of-Done items; k6 load tests gate certification.
AuthN/AuthZ
- Mechanisms: tenant API keys (scoped, rotatable) · public write keys (track-only, ship with widget F1b) · OAuth2 client credentials (integrations) · member tokens (native OTP/magic-link or token exchange
POST /v1/core/auth/token-exchange). - Authorization model (DEC-D5): every endpoint declares EXACTLY ONE permission
{module}.{resource}.{action}(e.g.,loyalty.redemptions.create). Permissions bundle into roles; roles attach to users AND machines (API keys / OAuth clients = machine roles). OAuth scopes = permission bundles shown on the consent screen. - Third parties are NEVER required to provide IPs: keys + HMAC + rate limits suffice. Per-tenant IP allowlist exists as opt-in hardening, default OFF.
Policy parameters
Every policy parameter is tenant-configurable and ships with a published default (OQ-LOY-14). Attribution windows, expiry horizons, grace periods, reservation TTLs, rollback limits, return windows, retry counts — a value we choose is a starting point, never a constraint the tenant has to live with. Two obligations follow: the default is documented in the public reference beside the parameter, and changing it takes effect from that moment forward, never retroactively over records already created under the old value. The exception is a parameter that governs a state machine or a correctness invariant. Those are not policy and are not configurable.Conventions
- Idempotency:
Idempotency-Keyheader REQUIRED onPOST /v1/loyalty/redemptionsand every mutation the docs mark idempotent; server stores result 24 h and replays it. - Errors: RFC 9457 problem+json with stable
code(e.g.,UNSUPPORTED_CODE,LIMIT_EXCEEDED,SUPPRESSED_RECIPIENT). - Rate limits: PUBLISHED per plan in docs; responses carry standard
RateLimit-*headers. - Deprecation: minimum 6-month window,
Sunsetheader, Mintlify changelog entry. Additive changes only within v1. - Golden rule: Softcrum’s own console, portal and widget consume EXCLUSIVELY this public API via packages/api-client. If our UI needs something the API lacks, the API is incomplete.