openapi: 3.1.0
info:
  title: Users & Access
  version: "1"
  description: Merchant portal users and embedded-dashboard access tokens.
tags:
  - name: Merchant users
    description: Portal users who can access a merchant.
  - name: Embedded dashboard
    description: Tokens and URLs for embedding the Datman merchant dashboard in your app.
paths:
  /v1/merchants/{merchantId}/users:
    get:
      operationId: listMerchantUsers
      summary: List merchant users
      description: Lists the portal users who have an active role on the merchant,
        with each user's display name, email, role and status. `status` is
        `Active` once the user has completed sign-up and `Pending` otherwise.
        Revoked and deleted users are excluded. The list is not paginated. The
        merchant must belong to your reseller, otherwise 404.
      tags:
        - Merchant users
      security:
        - ApiKeyAuth: []
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Numeric merchant id. Must be a positive integer belonging to your
            reseller.
          schema:
            type: integer
            minimum: 1
          example: 123456
      responses:
        "200":
          description: Users with access to the merchant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CpMerchantUsersResponse"
              examples:
                users:
                  summary: Owner and a pending staff member
                  value:
                    data:
                      - relationship_id: 9001
                        id: 5001
                        name: Alex Example
                        email: owner@example.com
                        roles:
                          - Owner
                        merchant_role_id: 1
                        reseller_id: 42
                        status: Active
                        last_login: "-"
                      - relationship_id: 9002
                        id: 5002
                        name: staff
                        email: staff@example.com
                        roles:
                          - Manager
                        merchant_role_id: 3
                        reseller_id: 42
                        status: Pending
                        last_login: "-"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The merchant does not belong to your reseller.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                notFound:
                  summary: Merchant not found or not in your reseller
                  value:
                    error:
                      code: NOT_FOUND
                      message: Merchant not found
        "422":
          description: The merchantId path segment is not a positive integer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidMerchantId:
                  summary: Non-numeric merchant id
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: merchantId is required
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    servers:
      - url: https://sandbox-portal.datmanlabs.com/api
        description: Sandbox
  /api/v1/bifrost/get-embed-token/{merchantId}:
    servers:
      - url: https://portal-api-sandbox.datmancrm.com/bifrost/sandbox
        description: Sandbox
      - url: https://portal-api.datmancrm.com/bifrost/prod
        description: Production
    get:
      operationId: getEmbedToken
      summary: Get an embedded dashboard session token
      description: Creates a short-lived (10 minute) session for the embedded merchant
        dashboard and returns its token. The merchant must belong to the calling
        partner.
      tags:
        - Embedded dashboard
      security:
        - ApiKeyAuth: []
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Datman merchant ID.
          schema:
            type: string
          example: "1000001"
      responses:
        "200":
          description: Session token created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmbedTokenResponse"
              examples:
                token:
                  summary: Session token
                  value:
                    token: 0123456789abcdef0123456789abcdef
                    expiresAt: 1767225600
        "401":
          description: Missing or invalid `api_key` header.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorisedError"
              examples:
                unauthorised:
                  summary: Unauthorised
                  value:
                    message: UNAUTHORISED
        "404":
          description: Merchant does not exist or does not belong to the calling partner.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageError"
              examples:
                notFound:
                  summary: Merchant not found
                  value:
                    message: Merchant does not exist
        "500":
          description: Unexpected server error. The body is the serialised error object,
            usually empty.
          content:
            application/json:
              schema:
                type: object
  /api/v1/bifrost/get-embededurl/{merchantId}/{page}:
    servers:
      - url: https://portal-api-sandbox.datmancrm.com/bifrost/sandbox
        description: Sandbox
      - url: https://portal-api.datmancrm.com/bifrost/prod
        description: Production
    get:
      operationId: getEmbedUrl
      summary: Get an embedded dashboard URL
      description: Creates a short-lived (10 minute) session for the embedded merchant
        dashboard and returns a ready-to-load URL for the requested page. The
        merchant must belong to the calling partner.
      tags:
        - Embedded dashboard
      security:
        - ApiKeyAuth: []
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Datman merchant ID.
          schema:
            type: string
          example: "1000001"
        - name: page
          in: path
          required: true
          description: Dashboard page to open.
          schema:
            type: string
            enum:
              - overview
              - payment
              - payout
          example: overview
      responses:
        "200":
          description: Embed URL created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmbedUrlResponse"
              examples:
                url:
                  summary: Embed URL for the overview page
                  value:
                    url: https://embed.example.com/overview?token=0123456789abcdef0123456789abcdef
                    expiresAt: 1767225600
        "401":
          description: Missing or invalid `api_key` header, or an unknown `page`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageError"
              examples:
                unauthorised:
                  summary: Unauthorised
                  value:
                    message: UNAUTHORISED
                unknownPage:
                  summary: Unknown page
                  value:
                    message: Page does not exist
        "404":
          description: Merchant does not exist or does not belong to the calling partner.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageError"
              examples:
                notFound:
                  summary: Merchant not found
                  value:
                    message: Merchant does not exist
        "500":
          description: Unexpected server error. The body is the serialised error object,
            usually empty.
          content:
            application/json:
              schema:
                type: object
components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable SCREAMING_SNAKE error code.
              examples:
                - UNAUTHORIZED
                - FORBIDDEN
                - VALIDATION_ERROR
                - RATE_LIMITED
                - INTERNAL_ERROR
                - DB_TIMEOUT
                - DB_CLIENT_CLOSED
            message:
              type: string
              description: Human-readable error message.
            details:
              description: Optional extra context about the error.
    CpMerchantUser:
      type: object
      required:
        - relationship_id
        - id
        - name
        - email
        - roles
        - merchant_role_id
        - reseller_id
        - status
        - last_login
      properties:
        relationship_id:
          type:
            - integer
            - string
          description: Id of the user-to-merchant relationship.
        id:
          type:
            - integer
            - string
          description: User id.
        name:
          type: string
          description: Display name. Falls back to the email local-part.
        email:
          type: string
        roles:
          type: array
          description: Role names on this merchant (currently at most one).
          items:
            type: string
        merchant_role_id:
          type:
            - integer
            - "null"
        reseller_id:
          type:
            - integer
            - "null"
        status:
          type: string
          enum:
            - Active
            - Pending
            - Revoked
          description: "`Active` once sign-up is complete, otherwise `Pending`."
        last_login:
          type: string
          description: Not tracked. Always `-`.
    CpMerchantUsersResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CpMerchantUser"
    UnauthorisedError:
      type: object
      properties:
        message:
          type: string
          example: UNAUTHORISED
    MessageError:
      type: object
      properties:
        message:
          type: string
          description: Error message.
    EmbedTokenResponse:
      type: object
      required:
        - token
        - expiresAt
      properties:
        token:
          type: string
          description: Session token (32 hex characters), valid for 10 minutes.
        expiresAt:
          type: integer
          description: Expiry time as a Unix timestamp in seconds.
    EmbedUrlResponse:
      type: object
      required:
        - url
        - expiresAt
      properties:
        url:
          type: string
          format: uri
          description: Embedded dashboard URL for the requested page, with the session
            token as the `token` query parameter.
        expiresAt:
          type: integer
          description: Expiry time as a Unix timestamp in seconds.
  responses:
    Unauthorized:
      description: Missing, invalid, expired or revoked credentials.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: UNAUTHORIZED
              message: Invalid, expired, or revoked API key
    Forbidden:
      description: The caller may not access this route or reseller (for example the
        API key has no reseller, or the body names a different reseller).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: FORBIDDEN
              message: Reseller not allowed for this caller
    RateLimited:
      description: The per-reseller rate limit was exceeded.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: RATE_LIMITED
              message: Rate limit exceeded
    InternalError:
      description: The query or a required configuration failed. Underlying details
        are not returned.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: INTERNAL_ERROR
              message: Query failed
    ServiceUnavailable:
      description: The database timed out or closed the connection. Safe to retry.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: DB_TIMEOUT
              message: The database did not respond in time. Please retry.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api_key
      description: Partner API key issued by Datman.
