> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Standard — Public API (v2)

> Canonical: https://api.softcrum.com/v1/{module}/{resource} (no /api segment). Modules: core, loyalty, messaging, crm (suite modules join as they expose public APIs).

## Base & structure

* Canonical: `https://api.softcrum.com/v1/{module}/{resource}` (no `/api` segment). Modules: `core`, `loyalty`, `messaging`, `crm` (suite modules join as they expose public APIs).
* NO Segment-style aliases in v1 (`/v1/track` does 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-Key` header REQUIRED on `POST /v1/loyalty/redemptions` and 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, `Sunset` header, 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.
