> ## Documentation Index
> Fetch the complete documentation index at: https://developers.perkstar.co.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth check + org context

> Confirms the API key is valid + returns the operator's org
context (name, currency, timezone) and the available cards.
First call every POS adapter makes on install.

Requires NO specific scope — any valid (non-revoked,
non-expired) key works. Brute-force / rate-limit protection
still applies.




## OpenAPI

````yaml /api/openapi.yaml get /marketplace/ping
openapi: 3.1.0
info:
  title: Perkstar Public REST API
  version: 2026-05-04.oauth
  description: |
    Programmatic access to your Perkstar loyalty data. Two authentication
    modes are supported:

    - **API key.** Per-org bearer token issued from Settings → API keys
      (`pk_live_…` / `pk_test_…`). Right choice for direct integrations,
      single-tenant POS adapters, and back-office sync.
    - **OAuth 2.0.** Three-legged authorization-code flow with PKCE,
      for marketplace listings (Square App Marketplace, Shopify App
      Store, Toast Partner Marketplace, Lightspeed). The merchant
      installs your app from the partner marketplace, lands on
      `/oauth/authorize`, approves the requested scopes, and your
      server exchanges the code at `/api/oauth/token` for a short-lived
      access token. See the dedicated OAuth section below.

    Every endpoint is scoped, rate-limited, and idempotent on POST.
    POS-adapter builders should start with the `/marketplace/*`
    endpoints — they collapse the typical "find-or-create customer +
    enrolment + post transaction" flow into a single round-trip with
    permanent dedupe via `external_transaction_id`.

    ## Test credentials

    Test credentials (`pk_test_…` or OAuth applications in TEST mode)
    can read API data, create isolated test transactions, and simulate
    wallet pushes. They cannot create, update, or delete live customers,
    enrolments, or webhook configuration. Test calls to
    `/marketplace/accrue` only work with an existing customer and
    enrolment; `/marketplace/enroll` is live-only. This prevents a
    sandbox integration from changing live customer records.

    Copy-paste cURL recipes are published at `/api/v1/curl-recipes.md`
    for teams that want to test the API before wiring the SDK.

    ## Response headers (universal)

    Every response, success or error, carries:

    - **X-Request-Id** — unique per request. Echo this in support
      tickets; we can find the exact call in the per-key audit log.
    - **X-API-Version** — date-versioned schema marker
      (`2026-05-04` as of writing). 12-month deprecation policy.
    - **X-RateLimit-Limit** — per-minute quota for the credential.
    - **X-RateLimit-Remaining** — tokens left in the current window.
    - **X-RateLimit-Reset** — unix-seconds when the window refills.
    - **Retry-After** — emitted on 429 only; seconds to wait.

    The reusable error responses below (`Unauthorized`, `Forbidden`,
    `NotFound`, `RateLimited`, `ValidationError`) declare these as
    strongly-typed `headers` so codegen against an error path has
    them. Success responses inline their `"200"` / `"201"` and rely
    on this convention rather than per-endpoint header declarations.
  contact:
    name: Perkstar
    url: https://perkstar.co.uk
servers:
  - url: https://dashboard.perkstar.co.uk/api/v1
    description: Production
security:
  - bearerAuth: []
paths:
  /marketplace/ping:
    get:
      tags:
        - Marketplace
      summary: Auth check + org context
      description: |
        Confirms the API key is valid + returns the operator's org
        context (name, currency, timezone) and the available cards.
        First call every POS adapter makes on install.

        Requires NO specific scope — any valid (non-revoked,
        non-expired) key works. Brute-force / rate-limit protection
        still applies.
      operationId: marketplacePing
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketplacePingResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - bearerAuth: []
components:
  schemas:
    MarketplacePingResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            organization:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
                slug:
                  type: string
                currency:
                  type: string
                timezone:
                  type: string
                default_card_id:
                  type: string
                  nullable: true
                  description: |
                    The single ACTIVE card on the org if there's
                    exactly one; otherwise null. When null, the
                    partner must pass `card_id` explicitly on accrue
                    / enroll calls.
            api_key:
              type: object
              properties:
                id:
                  type: string
                prefix:
                  type: string
                name:
                  type: string
                scopes:
                  type: array
                  items:
                    type: string
                mode:
                  type: string
                  enum:
                    - live
                    - test
                rate_limit_per_minute:
                  type: integer
            available_cards:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  slug:
                    type: string
                  type:
                    type: string
                    enum:
                      - STAMP
                      - POINTS
                      - CASHBACK
                      - MULTIPASS
                      - DISCOUNT
                      - COUPON
                      - MEMBERSHIP
                      - GIFT
                      - TICKET
            server_time:
              type: string
              format: date-time
    Error:
      type: object
      properties:
        error:
          type: object
          required:
            - type
            - message
            - code
          properties:
            type:
              type: string
              enum:
                - authentication_error
                - permission_error
                - rate_limit_error
                - validation_error
                - not_found
                - idempotency_error
                - server_error
            message:
              type: string
            code:
              type: string
            param:
              type: string
  responses:
    Unauthorized:
      description: Missing / invalid / expired API key
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
        X-API-Version:
          $ref: '#/components/headers/XAPIVersion'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Per-key rate limit exceeded
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
        X-API-Version:
          $ref: '#/components/headers/XAPIVersion'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  headers:
    XRequestId:
      description: |
        Unique id for this request. Echo this in your support tickets
        and we can trace the exact call in the audit log on the API key
        detail page.
      schema:
        type: string
    XAPIVersion:
      description: |
        Date-versioned schema marker. The current value is `2026-05-04`.
        Bumps follow the 12-month deprecation policy documented in the
        in-dashboard docs page.
      schema:
        type: string
    XRateLimitLimit:
      description: Total requests permitted per minute for this credential.
      schema:
        type: integer
    XRateLimitRemaining:
      description: Requests left in the current 60s window.
      schema:
        type: integer
    XRateLimitReset:
      description: Unix-seconds timestamp when the current window refills.
      schema:
        type: integer
    RetryAfter:
      description: |
        Seconds to wait before retrying. Only emitted on 429 responses.
      schema:
        type: integer
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: pk_live_… / pk_test_… or perk_at_…

````

## Related topics

- [Postman collection](/tools/postman.md)
- [Send a wallet push](/api-reference/pushes/send-a-wallet-push.md)
- [Authentication and scopes](/fundamentals/authentication.md)
- [Bulk-delete every test-mode transaction for the org](/api-reference/transactions/bulk-delete-every-test-mode-transaction-for-the-org.md)
- [Go-live checklist](/support/go-live-checklist.md)
