Skip to content
Last updated

Internal Transactions move funds book-to-book between two Monato accounts (same institution). These do not go through SPEI, so there’s no CEP and settlement is typically near-real-time when both instruments are active and funded.


Overview

  • Rail: Internal (book-to-book)
  • CEP: Not applicable
  • Latency: Near-real-time
  • Use cases:
    • Move funds between Cost Centers / Business Units
    • Intra-merchant payouts
    • Reserve account management
    • Move funds to another Monato customer

Parity with Money Out: Request body format and most validations are the same as Money Out, with a few extra internal checks noted below.


Endpoint

POST /v1/transactions/internal_transaction


Request

Path params: none
Query params: none

Headers

  • Authorization: Bearer <JWT>
  • Content-Type: application/json

Body

{
  "client_id": "c2d1d1e3-3340-4170-980e-e9269bbbc551",
  "source_instrument_id": "709448c3-7cbf-454d-a87e-feb23801269a",
  "destination_instrument_id": "dd7f8d89-94dd-43ca-871b-720fde378b52",
  "transaction_request": {
    "amount": "1.90",
    "currency": "MXN",
    "description": "Internal transfer",
    "external_reference": "1238766"
  }
}

Validations

Field-level

  • amount
    • Must be a numeric string with 2 decimal places (e.g., "1.90").
    • Must be greater than 0 (otherwise backend returns DATA_ERROR: "Transaction Amount must be higher than 0.").
  • currency
    • Only "MXN" is supported (otherwise: DATA_ERROR: "Transaction currency unsupported.").
  • description
    • Length < 40 characters (if exceeded: DATA_ERROR: "Transaction description must have less than 40 characters length.").
  • external_reference
    • Must be numeric and max 7 digits (otherwise: DATA_ERROR: "External reference should be numeric and have a maximum length of 7 digits.").
  • IDs
    • client_id, source_instrument_id, destination_instrument_id must be valid UUIDs.

Instrument state

  • Source
    • Exists, is active, not blocked, and has sufficient funds
      (if not: FAILED_PRECONDITION: "The account does not have sufficient funds.").
  • Destination
    • Exists / is assigned, active, and not blocked
      (if not: FAILED_PRECONDITION: "The account is not currently active.").
  • Same institution (internal rail)
    • Destination must be internal to Monato (if not: 409 external_transfer_not_allowed).
  • Different instruments
    • Best practice: source_instrument_id destination_instrument_id.

Response examples

Succed (LIQUIDATED)

{
    "id": "09c9caac-3b74-4690-8ac5-5a01b2559b3f",
    "bankId": "d3435bd9-998d-4e8a-9067-6b71d5fd3ac7",
    "clientId": "b000654b-4d12-46e5-b451-662459b6effc",
    "externalReference": "1238801",
    "trackingId": "20250925FINCHCUCHFGMRLZ",
    "description": "Prueba Internal 25/09/25",
    "amount": "1.00",
    "currency": "MXN",
    "category": "INTER_TRANS",
    "subCategory": "INT_DEBIT",
    "transactionStatus": "LIQUIDATED",
    "audit": {
        "createdAt": "2025-09-25 15:48:28.486316-06:00",
        "updatedAt": "2025-09-25 15:48:28.773897-06:00",
        "deletedAt": "None",
        "blockedAt": "None"
    }
}

Error Examples

Invactive account — 400 FAILED_PRECONDITION (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "FAILED_PRECONDITION",
            "domain": "CORE",
            "metadata": {
                "error_detail": "The account is not currently active.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Insufficient funds — 400 FAILED_PRECONDITION (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "FAILED_PRECONDITION",
            "domain": "CORE",
            "metadata": {
                "error_detail": "The account does not have sufficient funds.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Invalid External reference — 400 DATA_ERROR (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "DATA_ERROR",
            "domain": "CORE",
            "metadata": {
                "error_detail": "External reference should be numeric and have a maximum length of 7 digits.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Invalid amount (<= 0) — 400 DATA_ERROR (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "DATA_ERROR",
            "domain": "CORE",
            "metadata": {
                "error_detail": "Transaction Amount must be higher than 0.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Large description — 400 DATA_ERROR (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "DATA_ERROR",
            "domain": "CORE",
            "metadata": {
                "error_detail": "Transaction description must have less than 40 characters length.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Currency not supported — 400 DATA_ERROR (10-E4120)

{
    "code": 9,
    "message": "API Error",
    "details": [
        {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "DATA_ERROR",
            "domain": "CORE",
            "metadata": {
                "error_detail": "Transaction currency unsupported.",
                "http_code": "400",
                "module": "Transactions",
                "method_name": "InternalTransaction",
                "error_code": "10-E4120"
            }
        }
    ]
}

Error mapping (quick reference)

Failing ruleTypical response
Source account inactive/blocked400 FAILED_PRECONDITION (error_code: 10-E4120)
Insufficient funds (source)400 FAILED_PRECONDITION (error_code: 10-E4120)
external_reference non-numeric or > 7 digits400 DATA_ERROR (error_code: 10-E4120)
amount ≤ 0400 DATA_ERROR (error_code: 10-E4120)
description ≥ 40 characters400 DATA_ERROR (error_code: 10-E4120)
currency other than MXN400 DATA_ERROR (error_code: 10-E4120)
Destination not found / not assigned404 destination_not_found
Destination is external (not internal rail)409 external_transfer_not_allowed

⚠️ Disclaimer - We plan to unify the APIs in the future so that both external Money Out (SPEI) and internal transactions (between Monato accounts) are supported through a single endpoint. For now, please use the endpoints separately as documented.