Skip to main content

Control Plane API Reference

This specification documents the request bodies, responses, authentication scopes, and error semantics for the Profile, Settings, Team, and Support endpoint families exposed by @pcnaid/applepay-control-plane at https://api.lifecyclemanager.app. All routes require a valid Auth0 Bearer token (Authorization: Bearer <token>). Tenants are strictly isolated by auth.merchantId extracted from token claims; every database query parameterizes merchant_id = ${auth.merchantId}.

1. Profile (/applepay-lifecycle/v1/merchants)

GET /applepay-lifecycle/v1/merchants/me

  • Scope required: read:merchants
  • Description: Returns the authenticated merchant identity, operational configuration, tax identifier, and contact address.
  • Response 200 OK:

PATCH /applepay-lifecycle/v1/merchants/me

  • Scope required: write:merchants
  • Description: Updates merchant profile attributes including tax ID and full address object. Validates input lengths (businessName <= 255, contactName <= 255, contactPhone <= 50, taxId <= 50, address lines <= 255, postalCode <= 50, country <= 10).
  • Request Body:
  • Response 200 OK: Full merchant object (same shape as GET /merchants/me).
  • Errors: 400 Bad Request on validation failures (validation_error), 403 Forbidden if missing required user authorization.

2. Notification Settings (/applepay-lifecycle/v1/settings)

GET /applepay-lifecycle/v1/settings/notifications

  • Scope required: read:merchants
  • Description: Retrieves notification preferences for the authenticated member. Returns default preferences when no row has been persisted yet.
  • Response 200 OK:

PUT /applepay-lifecycle/v1/settings/notifications

  • Scope required: write:merchants
  • Description: Upserts notification preferences for the authenticated member in merchant_notification_settings. Accepts dashboard convenience keys (transactionAlerts, failureAlerts, weeklySummaries) or granular database flags.
  • Request Body:
  • Response 200 OK: Updated notification settings object.

GET /applepay-lifecycle/v1/settings

  • Scope required: read:merchants
  • Description: Unified endpoint returning both profile attributes and notification preferences in a single call.
  • Response 200 OK:

PATCH /applepay-lifecycle/v1/settings

  • Scope required: write:merchants
  • Description: Unified endpoint accepting partial { profile?, notifications? } payloads.
  • Response 200 OK: Unified settings object.

3. Team Management (/applepay-lifecycle/v1/team)

GET /applepay-lifecycle/v1/team

  • Scope required: read:merchants
  • Description: Lists all active team members and unexpired pending invitations for the merchant tenant.
  • Response 200 OK:

POST /applepay-lifecycle/v1/team/invitations

  • Scope required: write:merchants
  • Description: Generates and stores a new team invitation with a SHA-256 token hash and 7-day expiration. Rejects duplicate members or active open invitations with 409 Conflict.
  • Authorization: Requires caller to be an owner (via Auth0 claims or merchant_members role) or admin. Admins may only invite analysts (analyst). Owners may invite either admin or analyst. Callers with insufficient role receive 403 Forbidden ({ "error": "insufficient_role" }).
  • Delivery: Generates and returns a one-time inviteUrl directly in the 201 Created response, constructed as ${origin}/invite/accept?token=${token}. The dashboard origin is the optional DASHBOARD_ORIGIN variable when set; otherwise derived from ENVIRONMENT per URL law (production https://lifecyclemanager.app). The raw token is never logged and only its SHA-256 hash is persisted.
  • Request Body:
(Accepts "admin" or "analyst". "viewer" is automatically mapped to "analyst").
  • Response 201 Created:

DELETE /applepay-lifecycle/v1/team/invitations/:id

  • Scope required: write:merchants
  • Description: Deletes or revokes a pending invitation. The :id parameter must be a valid UUID; malformed IDs immediately return 404 Not Found ({ "error": "invitation_not_found" }). Restricted to the merchant owner (403 owner_only otherwise).
  • Response 200 OK:

PATCH /applepay-lifecycle/v1/team/members/:auth0UserId

  • Scope required: write:merchants
  • Description: Updates the role of a team member. Restricted to the merchant owner. Never allows demoting or reassigning the owner (400 cannot_demote_owner).
  • Request Body:
  • Response 200 OK: Updated member record.

DELETE /applepay-lifecycle/v1/team/members/:auth0UserId

  • Scope required: write:merchants
  • Description: Removes a member from the merchant team. Restricted to the merchant owner. The owner can never remove themselves or be removed (400 cannot_remove_owner).
  • Response 200 OK:

4. Support (/applepay-lifecycle/v1/support)

GET /applepay-lifecycle/v1/support/tickets

  • Scope required: read:merchants
  • Description: Lists all support tickets for the merchant tenant ordered by last update time, including initial message body.
  • Response 200 OK:

POST /applepay-lifecycle/v1/support/tickets

  • Scope required: write:merchants
  • Description: Opens a new support ticket and creates the initial message in support_ticket_messages within a single database transaction. Maps dashboard severity (medium -> normal) to priority. Validates subject (2-255 characters) and body (1-5000 characters).
  • Request Body:
  • Response 201 Created: Created ticket object with initial body.

GET /applepay-lifecycle/v1/support/tickets/:id

  • Scope required: read:merchants
  • Description: Retrieves a ticket by ID along with its full message history. The :id parameter must be a valid UUID; malformed IDs immediately return 404 Not Found ({ "error": "ticket_not_found" }). Returns 404 if the ticket does not exist or belongs to another merchant.
  • Response 200 OK:

POST /applepay-lifecycle/v1/support/tickets/:id/messages

  • Scope required: write:merchants
  • Description: Adds a new reply message to an existing ticket and bumps the ticket’s updated_at timestamp atomically in a single database transaction (sql.begin). The :id parameter must be a valid UUID; malformed IDs immediately return 404 Not Found ({ "error": "ticket_not_found" }).
  • Request Body:
  • Response 201 Created: