Developer guide · Public beta · v1
Activate app perks
Connect your backend to Earlybirdo with a small REST API. Customers enter their private code on your activation page; your server binds it once to their signed-in account.
Set up
- Open a saved app campaign in your dashboard. You can configure keys while it is a draft. Under App redemption API, create a named server key. Save it in your backend’s secret store; it is shown once and expires after 90 days.
- Map the campaign’s perk IDs to the access you promised, including duration and when the term starts. Preview returns the perk ID without using the code.
- Authenticate the customer in your app. Your backend derives an opaque account ID from that session, then shows the perk and account for confirmation before activating.
- Persist a unique operation ID and the exact request before redemption. Save the returned receipt and provision access once, with a unique receipt constraint in your database.
- Recheck receipt status on access and with a background reconciliation job. Rotate keys before expiry; replacement keys for the same campaign can read existing receipts.
Endpoints
Base URL: https://godkmybltlcvaaifrzdf.supabase.co/functions/v1/redemption-api/v1
Send Authorization: Bearer ebr_…. POST requests use JSON. Redemption also requires an Idempotency-Key header. No Supabase user token or service key is shared with developers.
| Request | Purpose |
|---|---|
POST /redemptions/preview | Validate a code and display its campaign and perk. Does not consume it. |
POST /redemptions | Consume the code with code and external_user_id. Returns 201, or 200 for a matching retry. |
GET /redemptions/{id} | Recheck a saved receipt using a key for its campaign. |
Codes contain 16 hexadecimal characters; spaces and hyphens are accepted. Account IDs use 1–128 letters, digits, underscores, dots, colons or hyphens, starting with a letter or digit. Use an internal ID, not an email. Idempotency keys use the same characters and are 8–128 characters long.
Server integration
import { createRedemptionClient } from './redemption-client.mjs';
const api = createRedemptionClient({
baseUrl: process.env.EARLYBIRDO_REDEMPTION_URL,
key: process.env.EARLYBIRDO_REDEMPTION_KEY,
});
// Your authenticated backend creates and persists this operation first.
// externalUserId comes from your verified session, never the request body.
const result = await api.redeem({
code: operation.code,
externalUserId: operation.userId,
idempotencyKey: operation.id,
});
// Persist result.data.id with a UNIQUE constraint; apply the perk once.
// Verify external_user_id, campaign_id and perk_id before provisioning.
// Grant only when status is "active" AND your own perk term is valid.
// A lost response? Retry the same persisted operation and payload.
const latest = await api.status(result.data.id);
// Reconcile access from latest.data.status, including suspended/revoked.Your activation endpoint needs session authentication, request forgery protection where applicable, and per-account rate limits. Do not accept the target account ID from the customer. Never log codes, keys or full activation request bodies.
Access and recovery
Active means the purchase currently supports access. It does not mean an unlimited subscription: enforce the purchased term in your own system. Suspended means access is paused, for example during a dispute, refund request, required delay response or campaign review. Revoked means the order was refunded or cancelled.
Use a bounded cache and reconciliation schedule that fits the beta limit of 120 requests per minute per campaign, shared across its keys and endpoints. On an outage or expired key, keep the last state recorded and retry; don’t grant new access or treat an error as proof of an active purchase. After your chosen maximum cache age, require a fresh status check.
Retry timeouts, connection failures, 429 and 503 with the original operation ID and payload. Honour Retry-After and use backoff. A retry returns the current receipt status, which may now be suspended or revoked. A 409 idempotency conflict means the payload changed; do not create a new operation to get around it.
An already-used code cannot bind to a second account. Recover a lost response with the original operation ID. Restore access on another device by signing into the same app account and checking its saved receipt. Account transfers require a deliberate seller support process; there is no transfer endpoint.
QR and in-person handover
The buyer’s order shows a private QR and readable code. A phone camera opens Earlybirdo’s seller page; scanning and previewing do not redeem anything. Only that campaign’s seller can confirm handover, which marks the entire order delivered. For several visits or deliveries, confirm after the final one. App activation uses the API above instead.
For app perks, use your authenticated web activation page unless your native distribution rules permit a code-entry flow. The API works behind either interface; no native SDK, MCP connector or CLI is required.
Native app distribution
Apple’s rules generally require In-App Purchase for digital unlocks and prohibit custom licence-key or QR unlock mechanisms under section 3.1.1. Google Play also generally requires its billing system for digital purchases, subject to its exceptions and regional programmes. This API does not exempt an app from those rules; choose an eligible flow for each store and region.