License API
License API
This page defines the redemption-code licensing API your app must expose so App Store can fulfill purchases. Implement these endpoints so customers receive access without NewRich storing pre-generated passwords.
API version: v1
Base path: /api/v1
Content type: application/json
Authentication: Caller domain allowlist + X-Licensing-Api-Key (see below)
Endpoints App Store calls
App Store stores the full versioned API root per product (for example https://dashboard.yourapp.com/api/v1) and appends resource paths:
| App Store action | HTTP call |
|---|---|
| Purchase fulfilled | POST {license_api_url}/redemption_codes |
| Refund / cancel (contract; integrate when available) | POST {license_api_url}/redemption_codes/revoke |
| Dispute resolved (contract; integrate when available) | POST {license_api_url}/redemption_codes/reactivate |
Authenticate App Store requests
Apply both checks below on all three licensing endpoints before processing the request body.
License API key
App Store sends a shared secret on every request. Your API must reject requests with a missing or incorrect key.
Request header: X-Licensing-Api-Key
Your configuration:
LICENSING_API_KEY=<provided privately by NewRich>
Compare the header value to your configured key using a constant-time comparison.
| HTTP | msg | When |
|---|---|---|
| 401 | License API key required. | Header absent or empty |
| 403 | License API key is invalid. | Header present but does not match |
| 503 | Licensing API is not configured. | LICENSING_API_KEY is not set |
Caller domain allowlist
Your API must also reject requests that do not come from an authorized App Store hostname.
Your configuration:
LICENSING_ALLOWED_DOMAINS=apps.newrich.com
App Store production requests identify themselves with hostname apps.newrich.com.
Resolve the caller hostname
Read the caller in this order (first match wins):
X-Licensing-Caller-Domain— preferred for server-to-server calls from App StoreOrigin— hostname extracted from the URLReferer— hostname extracted from the URL
Expect a hostname only (apps.newrich.com), not a full URL. Normalize case and strip ports when comparing.
Reject invalid callers
| HTTP | msg | When |
|---|---|---|
| 401 | Caller domain required. Send X-Licensing-Caller-Domain, Origin, or Referer. | No resolvable caller hostname |
| 403 | Caller domain is not authorized. | Hostname not in allowlist |
Response envelope
Use the same JSON shape on all licensing endpoints.
Success:
{
"success": true,
"msg": "ok",
"data": { }
}
Application error:
{
"success": false,
"msg": "Human-readable error message"
}
Validation error (HTTP 422):
{
"message": "The email field is required.",
"errors": {
"email": ["The email field is required."]
}
}
POST /redemption_codes — Generate
Issue an email-bound redemption code after purchase.
Request body
POST /api/v1/redemption_codes HTTP/1.1
Host: dashboard.yourapp.com
Content-Type: application/json
X-Licensing-Caller-Domain: apps.newrich.com
X-Licensing-Api-Key: <your-license-api-key>
{
"email": "[email protected]",
"order_id": "NR-42-17",
"product_sku": "yourapp-pro"
}
{
"email": "[email protected]",
"order_id": "NR-42-17",
"product_sku": "yourapp-pro"
}
| Field | Required | Type | Notes |
|---|---|---|---|
email | yes | string (email) | Must match at customer registration |
order_id | yes | string | Unique per order line; idempotency key |
product_sku | no | string | Optional audit field; tier logic is yours |
| Field source | Notes |
|---|---|
email | Billing email from checkout (lowercased and trimmed) |
order_id | Format NR-{order_id}-{order_item_id} — unique per line item |
Minimum success response (HTTP 2xx):
{
"success": true,
"data": {
"code": "YOUR-ABCD-EFGH-IJKL"
}
}
App Store requires HTTP 2xx, "success": true (boolean), and a non-empty string data.code. A fuller response is fine:
{
"success": true,
"msg": "ok",
"data": {
"code": "YOUR-ABCD-EFGH-IJKL",
"email": "[email protected]",
"order_id": "NR-42-17",
"status": "pending",
"current_upgrade": "Pro"
}
}
Behavior
- Idempotent on
order_id: duplicate generate calls return the same code (HTTP 200). - Revoked order: if the order was revoked, generate returns HTTP 409 with
msg: "Order has been revoked." - Code format: your choice; reference apps use a prefixed alphanumeric format.
status: start aspendinguntil the customer registers.
POST /redemption_codes/revoke — Revoke
Disable a code or linked user account (refunds, chargebacks).
Request body — provide one of:
{ "order_id": "NR-42-17" }
{ "code": "YOUR-ABCD-EFGH-IJKL" }
code is case-insensitive.
Success (200) — same data shape as generate.
| Code state | Effect |
|---|---|
pending | Status → revoked; registration blocked |
redeemed | Linked user disabled; code → revoked |
already revoked | Idempotent — return current state (200) |
| HTTP | msg |
|---|---|
| 404 | Redemption code not found. |
POST /redemption_codes/reactivate — Reactivate
Restore a previously revoked code or account.
Request body — same as revoke (order_id or code).
| Prior state | Effect |
|---|---|
| Revoked, never redeemed | Status → pending (customer can register) |
| Revoked after redemption | User re-enabled; code → redeemed |
| Not revoked | Idempotent — return current state (200) |
| HTTP | msg |
|---|---|
| 404 | Redemption code not found. |
Redemption code lifecycle
stateDiagram-v2
[*] --> pending: POST /redemption_codes
pending --> redeemed: Customer registers
pending --> revoked: POST .../revoke
redeemed --> revoked: POST .../revoke
revoked --> pending: POST .../reactivate (never redeemed)
revoked --> redeemed: POST .../reactivate (was redeemed)
| Status | Meaning |
|---|---|
pending | Issued; awaiting one-time registration |
redeemed | Customer created an account |
revoked | Cancelled or disabled |
Customer registration (your app)
Deliver the code from generate to the purchaser (App Store includes it in the order confirmation email). The customer completes registration at your public registration page.
Enforce at registration:
- Code must exist and be
pending - Email must match the
emailsent to generate (case-insensitive) - Code is one-time use
- Revoked codes are rejected
This step is outside the license API. App Store never calls a validate or activate endpoint.
Implementation checklist
-
X-Licensing-Api-Keyvalidation on all three routes (LICENSING_API_KEY, constant-time compare) - Caller domain allowlist on all three routes (
LICENSING_ALLOWED_DOMAINS) -
POST /api/v1/redemption_codeswithemail,order_id, optionalproduct_sku - Unique
order_idconstraint and idempotent generate - HTTP 409 when re-generating a revoked order
-
POST .../revokeandPOST .../reactivateacceptingorder_idorcode - Disable linked user on revoke; re-enable on reactivate
- Public registration page with email-bound, one-time code redemption
- Share license API base URL and customer app URL with NewRich for listing setup
Before go-live
NewRich verifies your integration before your listing goes live — authentication, code generation, idempotency, registration, revoke/reactivate, and an end-to-end purchase. During marketplace onboarding, the team will share a private verification checklist and test steps for your endpoints. Do not publish license API keys or ad-hoc test commands in public documentation or support channels.
Related
- Marketplace overview — roles and end-to-end flow