openapi: 3.0.0
info:
  title: Swypex Partner API
  version: '1.0'
tags:
  - name: Banks
  - name: Cards
  - name: Transfers
paths:
  /v1/bank:
    get:
      operationId: Banks_list
      description: |2-
          Get a list of all supported banks. Use the bank IDs from this endpoint
          when creating transfers.
      parameters: []
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Bank'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Banks
  /v1/card:
    get:
      operationId: Cards_list
      description: List cards with cursor pagination
      parameters:
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          explode: false
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            format: int32
            default: 10
          explode: false
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardListPage'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Cards
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/card/{id}:
    get:
      operationId: Cards_read
      description: Get a single card details
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Cards
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/card/{id}/limits:
    put:
      operationId: Limits_setLimit
      description: Set limits on a card
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/CommonWriteHeaders.idempotencyKey'
        - $ref: '#/components/parameters/CommonWriteHeaders.timestamp'
        - $ref: '#/components/parameters/CommonWriteHeaders.signature'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Cards
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema:
              $ref: '#/components/schemas/CardLimitsMergePatchUpdate'
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
    patch:
      operationId: Limits_updateLimit
      description: Update limits on a card
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/CommonWriteHeaders.idempotencyKey'
        - $ref: '#/components/parameters/CommonWriteHeaders.timestamp'
        - $ref: '#/components/parameters/CommonWriteHeaders.signature'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Card'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Cards
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema:
              $ref: '#/components/schemas/CardLimitsMergePatchUpdate'
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/transfer:
    get:
      operationId: Transfers_list
      description: |2-
          List all transfers with optional filtering and cursor-based pagination.
          Results are sorted by submission date in descending order (newest first).
      parameters:
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          explode: false
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 10
          explode: false
        - $ref: '#/components/parameters/TransferFilter.status'
        - $ref: '#/components/parameters/TransferFilter.minAmountCents'
        - $ref: '#/components/parameters/TransferFilter.maxAmountCents'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransferListPage'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Transfers
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
    post:
      operationId: Transfers_create
      description: |2-
          Create a new transfer. The transfer will be in PENDING status and require
          approval through your Swypex dashboard before processing.

          IMPORTANT:
          - Idempotency-Key header is REQUIRED to prevent duplicate transfers.
          - X-Swypex-Signature header is REQUIRED for request verification.
          If you retry a request with the same idempotency key within 24 hours, you will
          receive the original transfer response without creating a duplicate.
      parameters:
        - $ref: '#/components/parameters/TransferWriteHeaders.idempotencyKey'
        - $ref: '#/components/parameters/TransferWriteHeaders.timestamp'
        - $ref: '#/components/parameters/TransferWriteHeaders.signature'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Transfers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTransferRequest'
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/transfer/{id}:
    get:
      operationId: Transfers_read
      description: |2-
          Get details of a specific transfer by ID.
          Returns 404 if the transfer does not exist or you don't have access to it.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Transfers
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/transfer/{id}/cancel:
    post:
      operationId: Transfers_cancel
      description: |2-
          Cancel a pending transfer. Only transfers in PENDING status can be cancelled.
          Once a transfer is approved or processing, it cannot be cancelled through the API.

          IMPORTANT:
          - Idempotency-Key header is REQUIRED for safe cancellation.
          - X-Swypex-Signature header is REQUIRED for request verification.

          Returns 400 if the transfer is not in a cancellable state.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/TransferWriteHeaders.idempotencyKey'
        - $ref: '#/components/parameters/TransferWriteHeaders.timestamp'
        - $ref: '#/components/parameters/TransferWriteHeaders.signature'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Transfers
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
  /v1/transfer/{id}/execute:
    post:
      operationId: Transfers_execute
      description: |2-
          Execute a pending transfer. This approves the transfer and submits it for processing.
          Only transfers in PENDING status can be executed.

          After execution, the transfer status will change to PROCESSING as it is submitted
          to the payment network. The final status will be either SUCCEEDED or FAILED
          depending on the outcome.

          IMPORTANT:
          - Idempotency-Key header is REQUIRED for safe execution.
          - X-Swypex-Signature header is REQUIRED for request verification.
          - Ensure sufficient funds are available in your account before executing.

          Returns 400 if the transfer is not in PENDING status.
          Returns 400 if there are insufficient funds.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/TransferWriteHeaders.idempotencyKey'
        - $ref: '#/components/parameters/TransferWriteHeaders.timestamp'
        - $ref: '#/components/parameters/TransferWriteHeaders.signature'
      responses:
        '200':
          description: The request has succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Transfer'
        default:
          description: An unexpected error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
        - Transfers
      security:
        - OAuth2Auth:
            - cards:read
            - cards:write
            - transfers:read
            - transfers:write
components:
  parameters:
    CommonWriteHeaders.idempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: |2-
          An idempotency key to ensure the request is processed only once.
          This key should be unique for each request and will be stored for 24 hours.
          If a request with the same idempotency key is received within 24 hours,
          the original response will be returned without processing the request again.
      schema:
        type: string
        minLength: 12
        maxLength: 36
    CommonWriteHeaders.signature:
      name: X-Swypex-Signature
      in: header
      required: true
      description: |2-
          RSA-SHA256 signature for request verification, Base64 encoded.

          Signature computation:
          1. Concatenate: METHOD + PATH + TIMESTAMP (e.g., "POST/v1/transfer1699012345")
          2. Sign the concatenated string using your private RSA key with SHA256
          3. Base64 encode the signature

          Example (pseudocode):
          message = "POST/v1/transfer" + "1699012345"
          signature = base64(rsa_sha256_sign(private_key, message))

          Your RSA private key is provided separately during partner onboarding.
          This protects against man-in-the-middle and replay attacks.
      schema:
        type: string
    CommonWriteHeaders.timestamp:
      name: X-Swypex-Request-Timestamp
      in: header
      required: true
      description: |2-
          REQUIRED. Unix timestamp (seconds since epoch) of when the request was created.
          Used for replay attack prevention. Requests with timestamps older than 5 minutes
          will be rejected.
      schema:
        type: integer
        format: int64
    TransferFilter.maxAmountCents:
      name: maxAmountCents
      in: query
      required: false
      description: Filter by maximum amount in cents
      schema:
        type: integer
        format: int64
        minimum: 0
      explode: false
    TransferFilter.minAmountCents:
      name: minAmountCents
      in: query
      required: false
      description: Filter by minimum amount in cents
      schema:
        type: integer
        format: int64
        minimum: 0
      explode: false
    TransferFilter.status:
      name: status
      in: query
      required: false
      description: Filter by transfer status
      schema:
        $ref: '#/components/schemas/TransferStatus'
      explode: false
    TransferWriteHeaders.idempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: |2-
          REQUIRED. An idempotency key to ensure the request is processed only once.
          This key must be unique for each transfer request and will be stored for 24 hours.
          If a request with the same idempotency key is received within 24 hours,
          the original response will be returned without creating a new transfer.
      schema:
        type: string
        minLength: 12
        maxLength: 36
    TransferWriteHeaders.signature:
      name: X-Swypex-Signature
      in: header
      required: true
      description: |2-
          REQUIRED. RSA-SHA256 signature for request verification, Base64 encoded.

          Signature computation:
          1. Concatenate: METHOD + PATH + TIMESTAMP
             - For create: "POST/v1/transfer" + timestamp
             - For cancel: "POST/v1/transfer/{id}/cancel" + timestamp (with actual ID)
          2. Sign the concatenated string using your RSA private key with SHA256
          3. Base64 encode the resulting signature

          Example (pseudocode):
          timestamp = 1699012345
          message = "POST/v1/transfer" + str(timestamp)  // "POST/v1/transfer1699012345"
          signature = base64(rsa_sha256_sign(private_key, message))

          Your RSA private key is provided separately during partner onboarding.
          Swypex uses the corresponding public key to verify request authenticity.
          The timestamp inclusion prevents replay attacks.
      schema:
        type: string
    TransferWriteHeaders.timestamp:
      name: X-Swypex-Request-Timestamp
      in: header
      required: true
      description: |2-
          REQUIRED. Unix timestamp (seconds since epoch) of when the request was created.
          Used for replay attack prevention. Requests with timestamps older than 5 minutes
          will be rejected.
      schema:
        type: integer
        format: int64
  schemas:
    Bank:
      type: object
      required:
        - id
        - name
        - shortName
      properties:
        id:
          type: string
          description: Unique identifier for the bank
        name:
          type: string
          description: Full name of the bank
        shortName:
          type: string
          description: Short name of the bank
      description: Information about the bank receiving the transfer
      example:
        id: BNK1000000000
        name: Mashreq Bank
        shortName: MASH
    Card:
      type: object
      required:
        - id
        - last4
        - nickname
        - cardHolder
        - limits
        - availableBalance
      properties:
        id:
          type: string
          description: Unique identifier for the card
        last4:
          type: string
          nullable: true
          description: The last 4 digits of the card number. Null if the card is not activated
        nickname:
          type: string
          description: Nickname assigned to the card
        cardHolder:
          allOf:
            - $ref: '#/components/schemas/CardHolder'
          description: The cardholder information
        limits:
          allOf:
            - $ref: '#/components/schemas/CardLimits'
          description: The limits associated with the card
        availableBalance:
          type: integer
          format: int64
          nullable: true
          minimum: 0
          maximum: 99999999999
          description: |2-
              Available balance for the card in cents.

              NOTES:
               - For non-approval-based cards, this is the amount available for spending and is equal to the
                 minimum of daily, monthly, or atm limits minus the amount spent in the current period.

               - For approval-based cards, this is the amount available for spending and is equal
                 to the amount available to spend on the card, considering current card spending,
                 pending transactions, outstanding cash.

               - This field is null if the card is not activated
      description: Card resource with holder information and spending limits
      example:
        id: CRD1000000000
        last4: '1234'
        nickname: Business Travel Card
        cardHolder:
          id: USR1000000000
          fullName: Sara Amr
        limits:
          daily: 500000
          monthly: 2000000
          atm: 400000
          approvalBased: null
        availableBalance: 1500000
    CardHolder:
      type: object
      required:
        - id
        - fullName
      properties:
        id:
          type: string
          description: Unique identifier for the card holder
        fullName:
          type: string
          description: Full name of the card holder
      description: Information about the card holder
      example:
        id: USR1000000000
        fullName: Sara Amr
    CardLimits:
      type: object
      required:
        - daily
        - monthly
        - atm
        - approvalBased
      properties:
        daily:
          type: integer
          format: int64
          minimum: -100000000
          maximum: 100000000
          description: Daily spending limits in cents
        monthly:
          type: integer
          format: int64
          minimum: -300000000
          maximum: 300000000
          description: Monthly spending limits in cents
        atm:
          type: integer
          format: int64
          minimum: -300000000
          maximum: 300000000
          description: Monthly ATM withdrawal limits in cents
        approvalBased:
          type: integer
          format: int64
          nullable: true
          minimum: -99999999999
          maximum: 99999999999
          description: Approval-based spending limits in cents. Null if card is not approval-based
      description: |2-
          Spending limits associated with a card.
          If card is an approval-based card, then all limits except approval_based will
          be set to maximum allowed spending limit and are not useful.
      example:
        daily: 500000
        monthly: 2000000
        atm: 400000
        approvalBased: 10000
    CardLimitsMergePatchUpdate:
      type: object
      properties:
        daily:
          type: integer
          format: int64
          minimum: -100000000
          maximum: 100000000
          description: Daily spending limits in cents
        monthly:
          type: integer
          format: int64
          minimum: -300000000
          maximum: 300000000
          description: Monthly spending limits in cents
        atm:
          type: integer
          format: int64
          minimum: -300000000
          maximum: 300000000
          description: Monthly ATM withdrawal limits in cents
        approvalBased:
          type: integer
          format: int64
          nullable: true
          minimum: -99999999999
          maximum: 99999999999
          description: Approval-based spending limits in cents. Null if card is not approval-based
      description: ''
    CardListPage:
      type: object
      required:
        - cards
      properties:
        cards:
          type: array
          items:
            $ref: '#/components/schemas/Card'
        nextCursor:
          type: string
      description: Paginated list of cards
    Category:
      type: string
      enum:
        - AGRICULTURE
        - ART
        - AUTOMOTIVE
        - CASH
        - CHARITY
        - CLOTHING
        - CONSTRUCTION
        - DESIGN
        - E_COMMERCE
        - EDUCATION
        - ENERGY
        - ENGINEERING
        - ENTERTAINMENT
        - FINANCIAL_SERVICES
        - FITNESS
        - FUEL
        - FOOD_BEVERAGE
        - GOVERNMENT
        - HOSPITALITY
        - INSURANCE
        - LEGAL
        - LOGISTICS
        - MANUFACTURING
        - MARKETING
        - MEDIA
        - MEDICAL
        - MEMBERSHIP
        - PHARMACEUTICAL
        - PROFESSIONAL_SERVICES
        - REAL_ESTATE
        - RETAIL
        - TECHNOLOGY
        - TOWING
        - TRANSPORTATION
        - TRAVEL
        - UTILITIES
        - OTHER
    CreateTransferRequest:
      type: object
      required:
        - amountCents
        - currency
        - bankId
        - accountNumber
        - accountName
        - category
      properties:
        amountCents:
          type: integer
          format: int64
          minimum: 1
          maximum: 99999999999
          description: Amount in cents (e.g., 50000 = $500.00). Must be positive
        currency:
          allOf:
            - $ref: '#/components/schemas/Currency'
          description: Currency of the transfer
        bankId:
          type: string
          minLength: 1
          maxLength: 50
          description: Bank ID for the recipient. Use the /banks endpoint to get valid bank IDs
        accountNumber:
          type: string
          minLength: 1
          maxLength: 50
          description: Bank account number of the recipient
        accountName:
          type: string
          minLength: 1
          maxLength: 100
          description: Name on the bank account
        category:
          allOf:
            - $ref: '#/components/schemas/Category'
          description: Category for the transfer
        memo:
          type: string
          maxLength: 100
          description: Optional memo for the transfer. Maximum 100 characters
        note:
          type: string
          maxLength: 500
          description: Optional note for the transfer. Maximum 500 characters
      description: Input for creating a new transfer
      example:
        amountCents: 50000
        currency: EGP
        bankId: BNK1000000000
        accountNumber: '1234567890'
        accountName: Acme Corp
        category: MEDIA
        memo: Invoice 1001
        note: Payment for services rendered
    Currency:
      type: string
      enum:
        - EGP
      description: Currency code in ISO 4217 format
    Error:
      type: object
      required:
        - message
        - type
      properties:
        message:
          type: string
          description: Human-readable error message
        type:
          allOf:
            - $ref: '#/components/schemas/ErrorType'
          description: Machine-readable error type for programmatic handling
        details:
          type: string
          description: Additional context or details about the error, if available
      description: Standard error response for API operations
    ErrorType:
      type: string
      enum:
        - NotFound
        - InvalidRequest
        - InternalError
        - InsufficientFunds
        - InvalidBankAccount
        - DuplicateIdempotencyKey
        - Unauthorized
        - InvalidSignature
        - MissingSignature
        - MissingIdempotencyKey
      description: Error types for API responses
    Transfer:
      type: object
      required:
        - id
        - idempotencyKey
        - amountCents
        - currency
        - status
        - category
        - submissionDate
        - payee
      properties:
        id:
          type: string
          description: Unique identifier for the transfer
        idempotencyKey:
          type: string
          description: The idempotency key provided when creating the transfer
        amountCents:
          type: integer
          format: int64
          minimum: 1
          maximum: 99999999999
          description: Amount in cents (e.g., 50000 = $500.00)
        currency:
          allOf:
            - $ref: '#/components/schemas/Currency'
          description: Currency of the transfer
        status:
          allOf:
            - $ref: '#/components/schemas/TransferStatus'
          description: Current status of the transfer
        category:
          allOf:
            - $ref: '#/components/schemas/Category'
          description: Transfer category
        submissionDate:
          type: integer
          format: int64
          description: Unix timestamp (milliseconds) when the transfer was submitted
        reviewedAt:
          type: integer
          format: int64
          description: Unix timestamp (milliseconds) when reviewed (approved/rejected). Null if not yet reviewed
        cancelledAt:
          type: integer
          format: int64
          description: Unix timestamp (milliseconds) when cancelled. Null if not cancelled
        payee:
          allOf:
            - $ref: '#/components/schemas/TransferPayee'
          description: Information about the transfer recipient
        memo:
          type: string
          maxLength: 100
          description: Optional memo attached to the transfer. Maximum 100 characters
        note:
          type: string
          description: Optional note attached to the transfer
      description: |2-
          A transfer represents a payment from your company to an external bank account.
          All transfers follow a maker-checker approval workflow for security.
      example:
        id: TRF1000000000
        idempotencyKey: unique-key-123
        amountCents: 50000
        currency: EGP
        status: SUCCEEDED
        category: MEDIA
        submissionDate: 1704067200000
        reviewedAt: 1704070800000
        payee:
          accountNumber: '1234567890'
          accountName: Acme Corp
          bank:
            id: BNK1000000000
            name: Mashreq Bank
            shortName: MASH
    TransferListPage:
      type: object
      required:
        - transfers
      properties:
        transfers:
          type: array
          items:
            $ref: '#/components/schemas/Transfer'
        nextCursor:
          type: string
      description: Paginated list of transfers
    TransferPayee:
      type: object
      required:
        - accountNumber
        - accountName
        - bank
      properties:
        accountNumber:
          type: string
          description: Bank account number of the recipient. Can be IBAN or Phone number for wallet transfers
        accountName:
          type: string
          description: Name on the bank account
        bank:
          allOf:
            - $ref: '#/components/schemas/Bank'
          description: Bank information for the recipient account
      description: Information about the recipient of a transfer
      example:
        accountNumber: '1234567890'
        accountName: Acme Corp
        bank:
          id: BNK1000000000
          name: Mashreq Bank
          shortName: MASH
    TransferStatus:
      type: string
      enum:
        - PENDING
        - CANCELLED
        - REJECTED
        - PROCESSING
        - FAILED
        - SUCCEEDED
      description: The status of a transfer in the system
  securitySchemes:
    OAuth2Auth:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://p.swypex.com/v1/oauth/token
          scopes:
            cards:read: ''
            cards:write: ''
            transfers:read: ''
            transfers:write: ''
servers:
  - url: https://p.swypex.com
    description: Swypex Partner API Server
    variables: {}
