Skip to content

API Documentation

Cloud Functions (Callable)

All functions use Firebase Callable Functions (HTTPS-callable).

Authentication

All functions require Firebase Authentication. Pass Authorization: Bearer <idToken> header or use Firebase SDK.


issue_giftcard

Issue a new giftcard to a user.

Function: issue_giftcard
Role: ADMIN only

Request

{
  "idempotencyKey": "uuid-...",
  "productId": "giftcard-100nok",
  "targetUserId": "user-...",
  "claimCode": "XXXX-XXXX-XXXX-XXXX",
  "reason": "Birthday gift"
}

Response

{
  "success": true,
  "walletItemId": "uuid-...",
  "ledgerTxId": "uuid-...",
  "claimCodeHash": "sha256(code+pepper)"
}

Behavior

  1. Acquires atomic idempotency lock.
  2. Validates product exists.
  3. Creates wallet item.
  4. Creates ledger entry (ISSUE).
  5. If claim code provided: creates giftcard_codes/{codeHash}.
  6. Updates wallet projection.
  7. Logs admin action to audit log.

Errors

  • unauthenticated: User not authenticated.
  • permission-denied: User is not ADMIN.
  • invalid-argument: Missing required fields.
  • not-found: Product not found.
  • failed-precondition: Idempotency key already being processed.

redeem_value

Deduct value from a wallet item.

Function: redeem_value
Role: END_USER (own item) or STAFF/ADMIN (any item)

Request

{
  "idempotencyKey": "uuid-...",
  "walletItemId": "uuid-...",
  "amount": 50,
  "reason": "Coffee purchase"
}

Response

{
  "success": true,
  "ledgerTxId": "uuid-...",
  "remainingBalance": 50
}

Behavior

  1. Acquires idempotency lock.
  2. Fetches wallet item, verifies ownership.
  3. Checks status (must be ACTIVE).
  4. Checks expiry.
  5. Validates sufficient balance.
  6. Creates ledger entry (REDEEM).
  7. Updates wallet item with used amount.
  8. Creates redemption record.
  9. Updates wallet projection.
  10. Returns remaining balance.

Errors

  • unauthenticated: User not authenticated.
  • not-found: Wallet item not found.
  • permission-denied: User does not own the item.
  • failed-precondition: Item not ACTIVE, expired, or insufficient balance.

claim_code

Claim a giftcard using a code.

Function: claim_code
Role: END_USER

Request

{
  "idempotencyKey": "uuid-...",
  "code": "XXXX-XXXX-XXXX-XXXX",
  "externalAccountId": "membership-123"
}

Response

{
  "success": true,
  "walletItemId": "uuid-...",
  "ledgerTxId": "uuid-..."
}

Behavior

  1. Acquires idempotency lock.
  2. Hashes code and looks up in giftcard_codes.
  3. Validates code is ACTIVE and not expired.
  4. Links external account if provided.
  5. Creates new wallet item (copy of original, linked to claimant).
  6. Creates ledger entry (CLAIM).
  7. Marks code as CLAIMED.
  8. Updates wallet projection.

Errors

  • unauthenticated: User not authenticated.
  • not-found: Code not found.
  • failed-precondition: Code already claimed or blocked.

Idempotency

All write operations require idempotencyKey.

  • Format: UUID (RFC 4122) or any unique string.
  • Lifetime: 7 days (configurable).
  • Retry: Send same idempotencyKey + payload to get cached result.
  • Change payload: If you modify the request, behavior is undefined (hash mismatch detected).

Example: Retry Pattern

const idempotencyKey = generateUUID();

// First attempt
try {
  const result = await issueGiftCard({
    idempotencyKey,
    productId: "giftcard-100nok",
    targetUserId: "user-123"
  });
} catch (error) {
  if (error.code === "failed-precondition") {
    // Network error or server timeout - retry with SAME key
    const result = await issueGiftCard({
      idempotencyKey, // Same key!
      productId: "giftcard-100nok",
      targetUserId: "user-123"
    });
    // Will return cached result from first attempt
  }
}

Errors

Standard Firebase error format:

{
  "code": "string",
  "message": "Human-readable error"
}

Common codes: - unauthenticated: Missing/invalid auth token. - permission-denied: Insufficient permissions. - invalid-argument: Invalid request data. - not-found: Resource doesn't exist. - failed-precondition: Invalid state (e.g., item already redeemed). - internal: Server error (see logs).


Rate Limiting

No explicit rate limiting implemented. Recommend: - Firebase pricing (pay-per-read/write). - Add Cloud Armor rules if needed. - Monitor logs for abuse.


Pagination

Not implemented in v1. Recommend: - Firestore pagination via startAfter() and limit. - Implement in client or API layer.


Next Steps

  1. REST wrapper (Express.js or similar).
  2. GraphQL layer (optional).
  3. SDK for client apps (web, mobile).