openapi: 3.1.0
info:
  title: Transfers
  version: "1"
  description: Move funds between your merchants’ Datman accounts and between
    balance accounts, with a webhook when a transfer settles or fails.
tags:
  - name: Internal transfers
    description: Move funds between two merchants and get notified when the transfer
      completes.
paths:
  /api/v1/transfer/internal:
    servers:
      - url: https://sandbox-payments.datmanpay.com/rest
        description: Sandbox (base path from docs; verify)
      - url: https://payments.datmanpay.com/rest
        description: Production (base path inferred; verify)
    post:
      operationId: createInternalTransfer
      summary: Transfer funds between two merchants
      description: >-
        Moves `amount` from the source merchant's balance to the destination
        merchant's balance. Both merchants must be enabled for internal
        transfers and be in the same country.


        The transfer is processed asynchronously: the response only confirms it
        was accepted. Datman then sends a [transfer
        succeeded](/api-explorer/transfers/1/webhook/internalTransferSucceeded)
        or [transfer
        failed](/api-explorer/transfers/1/webhook/internalTransferFailed)
        webhook to `webhook_url`.
      tags:
        - Internal transfers
      security:
        - ApiTokenAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InternalTransferRequest"
            examples:
              deliveryFee:
                summary: Transfer a delivery fee
                value:
                  source_merchant_id: "663412"
                  destination_merchant_id: "663500"
                  amount: 350
                  transfer_category_code: 1
                  description: Delivery fee for order ORD-100245
                  reference: REF-100245-DLV
                  webhook_url: https://partner.example.com/webhooks/transfers
                  order_id: ORD-100245
      responses:
        "202":
          description: The transfer was accepted and is being processed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InternalTransferAccepted"
              examples:
                accepted:
                  summary: Transfer accepted
                  value:
                    request_id: 3f1c2b6a-8d4e-4f0a-9b7c-1e2d3c4b5a69
                    reference: REF-100245-DLV
                    status: processing
        "400":
          description: The request is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorCodeResponse"
              examples:
                sameMerchant:
                  summary: Same source and destination
                  value:
                    error_code: SAME_SOURCE_AND_DESTINATION_MERCHANT
                    message: Source and destination merchant cannot be the same
                validation:
                  summary: Validation error
                  value:
                    message: '"Source Merchant ID" is required'
        "401":
          description: The `api_token` header is missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorCodeResponse"
              examples:
                invalidToken:
                  summary: Missing or invalid api_token
                  value:
                    error_code: UNAUTHORIZED
                    message: Token is empty or Invalid token
        "403":
          description: "The transfer isn't allowed: a merchant isn't enabled for internal
            transfers, the merchants are in different countries or have
            different customer types, internal transfers aren't enabled for the
            country, or the amount is over the limit."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorCodeResponse"
              examples:
                senderNotEnabled:
                  summary: Source merchant not enabled
                  value:
                    error_code: INTERNAL_TRANSFER_SENDER_NOT_ENABLED
                    message: Internal transfer sender feature is not enabled for this merchant
                overLimit:
                  summary: Amount over the limit
                  value:
                    error_code: AMOUNT_EXCEEDED_THRESHOLD
                    message: Cannot transfer amount exceeding the threshold limit
                differentCountry:
                  summary: Merchants in different countries
                  value:
                    error_code: MERCHANTS_DIFFERENT_COUNTRY
                    message: Source and destination merchants must belong to the same country
        "404":
          description: The source or destination merchant doesn't exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorCodeResponse"
              examples:
                sourceNotFound:
                  summary: Source merchant not found
                  value:
                    error_code: SOURCE_MERCHANT_NOT_FOUND
                    message: Source merchant not found
                destinationNotFound:
                  summary: Destination merchant not found
                  value:
                    error_code: DESTINATION_MERCHANT_NOT_FOUND
                    message: Destination merchant not found
        "500":
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"
              example:
                message: Unable to process your request at the moment. Please try again later.
webhooks:
  internalTransferSucceeded:
    post:
      operationId: internalTransferSucceededWebhook
      summary: Internal transfer succeeded
      description: Datman sends a JSON POST to the `webhook_url` of an internal
        transfer when the funds have moved. Verify the `X-Datman-Signature`
        header over the raw request body before trusting the payload. The
        notification is sent once and isn't retried.
      tags:
        - Internal transfers
      security: []
      parameters:
        - $ref: "#/components/parameters/DatmanSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InternalTransferSucceededWebhook"
            examples:
              succeeded:
                summary: Transfer succeeded
                value:
                  request_id: 3f1c2b6a-8d4e-4f0a-9b7c-1e2d3c4b5a69
                  status: success
                  currency: GBP
                  source_merchant_id: "663412"
                  destination_merchant_id: "663500"
                  amount: 350
                  transfer_category_code: 1
                  description: Delivery fee for order ORD-100245
                  reference: REF-100245-DLV
                  webhook_url: https://partner.example.com/webhooks/transfers
                  order_id: ORD-100245
      responses:
        "200":
          description: Acknowledge receipt of the notification.
  internalTransferFailed:
    post:
      operationId: internalTransferFailedWebhook
      summary: Internal transfer failed
      description: Datman sends a JSON POST to the `webhook_url` of an internal
        transfer when the transfer fails. The body has a `reason` and no
        `status`. Verify the `X-Datman-Signature` header over the raw request
        body before trusting the payload. The notification is sent once and
        isn't retried.
      tags:
        - Internal transfers
      security: []
      parameters:
        - $ref: "#/components/parameters/DatmanSignature"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InternalTransferFailedWebhook"
            examples:
              insufficientFunds:
                summary: Insufficient funds
                value:
                  reason: Insufficient funds
                  request_id: 3f1c2b6a-8d4e-4f0a-9b7c-1e2d3c4b5a69
                  currency: GBP
                  source_merchant_id: "663412"
                  destination_merchant_id: "663500"
                  amount: 350
                  transfer_category_code: 1
                  description: Delivery fee for order ORD-100245
                  reference: REF-100245-DLV
                  webhook_url: https://partner.example.com/webhooks/transfers
                  order_id: ORD-100245
      responses:
        "200":
          description: Acknowledge receipt of the notification.
components:
  schemas:
    MessageResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
    InternalTransferRequest:
      type: object
      additionalProperties: false
      required:
        - source_merchant_id
        - destination_merchant_id
        - amount
        - transfer_category_code
        - description
        - reference
        - webhook_url
      properties:
        source_merchant_id:
          type: string
          pattern: ^[0-9]+$
          description: The merchant the funds come from, as a string of digits.
        destination_merchant_id:
          type: string
          pattern: ^[0-9]+$
          description: The merchant the funds go to, as a string of digits.
        amount:
          type: integer
          minimum: 1
          description: The amount in minor units (e.g. 350 is £3.50).
        transfer_category_code:
          type: integer
          minimum: 1
          description: The category of the transfer, as agreed with Datman.
        description:
          type: string
        reference:
          type: string
          description: Your reference for the transfer. It is returned in the webhook.
        webhook_url:
          type: string
          format: uri
          maxLength: 2048
          description: Where Datman sends the transfer result. Use HTTPS.
        order_id:
          type:
            - string
            - integer
          description: The order the transfer relates to, if any.
    InternalTransferAccepted:
      type: object
      required:
        - request_id
        - reference
        - status
      properties:
        request_id:
          type: string
          description: Datman's id for the transfer. It is returned in the webhook.
        reference:
          type: string
        status:
          type: string
          const: processing
    ErrorCodeResponse:
      type: object
      required:
        - message
      properties:
        error_code:
          type: string
          description: Machine-readable error code. Not present on validation errors.
        message:
          type: string
    InternalTransferWebhookBase:
      type: object
      properties:
        request_id:
          type: string
          description: The `request_id` returned when you created the transfer.
        source_merchant_id:
          type:
            - string
            - integer
        destination_merchant_id:
          type:
            - string
            - integer
        amount:
          type: integer
          description: The amount in minor units.
        currency:
          type: string
          description: ISO 4217 currency code. Not present for every transfer.
        transfer_category_code:
          type: integer
        description:
          type: string
        reference:
          type: string
          description: Your reference for the transfer.
        webhook_url:
          type: string
        order_id:
          type:
            - string
            - integer
          description: Present only if you sent it.
    InternalTransferSucceededWebhook:
      allOf:
        - $ref: "#/components/schemas/InternalTransferWebhookBase"
        - type: object
          required:
            - request_id
            - status
          properties:
            status:
              type: string
              const: success
    InternalTransferFailedWebhook:
      allOf:
        - $ref: "#/components/schemas/InternalTransferWebhookBase"
        - type: object
          required:
            - request_id
            - reason
          properties:
            reason:
              type: string
              description: Why the transfer failed, e.g. "Insufficient funds" or "Transfer
                failed".
  securitySchemes:
    ApiTokenAuth:
      type: apiKey
      in: header
      name: api_token
      description: API token issued by Datman.
  parameters:
    DatmanSignature:
      name: X-Datman-Signature
      in: header
      required: true
      description: HMAC-SHA256 of the raw JSON body using your webhook secret, hex-encoded.
      schema:
        type: string
