{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base",
  "info": {
    "title": "Grip Rails business clients API (phase 1)",
    "version": "2026-10-15",
    "summary": "Payment accounts in Argentine pesos, CVUs, transfers through COELSA and events for business clients of FCM Payment Solutions S.A.S.",
    "description": "API through which business clients of **FCM Payment Solutions S.A.S.** (a payment service provider that offers payment\naccounts, registered with the BCRA, the Central Bank of Argentina; PSP 214 at COELSA, the Argentine clearing house for\nimmediate transfers) operate **payment accounts in Argentine pesos**: check balances, use their CVUs (Argentine virtual\naccount numbers), validate counterparties, order transfers through COELSA and learn about incoming credits.\n\nThis is the **phase 1** contract, multi-client from day one: each entity has its own credentials, signing keys,\nwebhook URL, ledger accounts, CVUs, limits and fee. This contract is stable: the implementation may change and the client's integration does not have to.\n\n## What this API does not do\n\n- **It does not handle digital assets.** There are no crypto-assets, stablecoins, blockchain addresses, conversions or\n  quotes. The BCRA's consolidated rules for payment service providers (section 4.6) prohibit FCM Payment Solutions S.A.S.\n  from carrying them out or facilitating them. If a client operates crypto, that leg is executed by another regulated\n  legal entity and FCM Payment Solutions S.A.S. only moves pesos against instructions.\n- **It does not handle foreign currency or cross-border transfers.** The only currency is `ARS`, and destinations are\n  CBUs, CVUs or aliases of the Argentine system.\n- **It does not create CVUs or accounts in phase 1.** Grip Rails creates them at the entity's request (they show up as\n  listable account numbers). Creation through the API is phase 2.\n\nFCM Payment Solutions S.A.S. limits itself to offering payment services and is not authorized by the BCRA to operate as\na financial institution. Funds deposited in payment accounts do not constitute deposits in a financial institution, nor\ndo they have any of the guarantees that such deposits may enjoy under the laws and regulations applicable to deposits in\nfinancial institutions.\n\n## Environments\n\n| | Production | Sandbox |\n|---|---|---|\n| Host | `https://api.fcmpayments.com` | `https://empresas-sandbox.fcmpayments.com` |\n| API keys | `fcm_live_…` | `fcm_test_…` |\n| Money | Real. Transfers through COELSA (the Argentine clearing house) are irreversible. | Simulated. The sandbox has its own ledger and rail. |\n| Simulators (`/v1/simulations/*`) | Do not exist (404) | Yes |\n\nA key from one environment used in the other returns `403 environment_mismatch`.\n\n## Authentication: API key and a signature on every request\n\nEvery request carries **both**:\n\n1. `Authorization: Bearer fcm_live_…`: the entity's API key. Grip Rails stores it hashed. Two can be active at once\n   so you can rotate without downtime.\n2. **An HTTP signature of the request (RFC 9421, HTTP Message Signatures)** made with the client's private key. Grip\n   Rails stores only the public key, registered at onboarding (up to two active per environment). Algorithms:\n   `ed25519` or `ecdsa-p256-sha256`.\n   - Required signed components: `\"@method\"` and `\"@target-uri\"`. If the request has a body, also\n     `\"content-type\"`, `\"content-digest\"` and `\"idempotency-key\"`. If you send `FCM-Version`, also\n     `\"fcm-version\"`. **`\"authorization\"` is not signed**: a signature that covers it is rejected with\n     `signature_invalid`. That way Grip Rails can re-verify the signature of each instruction with your public key,\n     without your API key, which it only stores hashed.\n   - Required signature parameters: `created` (Unix seconds; ±300 s accepted), `nonce` (16 to 64\n     base64url characters, unique per key for 10 minutes), `keyid` (the id of your public key, `ck_…`) and `alg`.\n   - With a body: `Content-Digest: sha-256=:…:` (RFC 9530) computed over the exact bytes you send.\n\n```http\nPOST /v1/outgoing_transfers HTTP/1.1\nHost: api.fcmpayments.com\nAuthorization: Bearer fcm_live_3kTq…\nContent-Type: application/json\nIdempotency-Key: 01JA2B3C4D5E6F7G8H9JKMNPQR\nContent-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:\nSignature-Input: sig1=(\"@method\" \"@target-uri\" \"content-type\" \"content-digest\" \"idempotency-key\");created=1760112000;nonce=\"Vq3n8sZx0bR1yTqLkM2pWg\";keyid=\"ck_01JA2B3C4D5E6F7G8H9JKMNPQS\";alg=\"ed25519\"\nSignature: sig1=:Kp9…==:\n```\n\nIn production the entity can request an IP allowlist. mTLS is offered as an additional layer in phase 2 for those\nwho require it; it does not replace the signature.\n\n## Idempotency\n\n- `Idempotency-Key` is **required on every `POST`** (16 to 128 characters `A-Z a-z 0-9 . _ : -`; a UUID or ULID is\n  recommended). Its scope is the entity in that environment.\n- Same key and same request: same response (status code and body), with `Idempotent-Replayed: true`.\n- Same key with a different request (SHA-256 fingerprint of method, path and body canonicalized per RFC 8785):\n  `409 idempotency_key_reused`.\n- While the first request is in progress: `409 idempotency_request_in_progress` (retryable).\n- `5xx` responses are not stored: retry with the **same** key.\n- The full record lives for **35 days** and can be looked up with `GET /v1/idempotency_keys/{idempotency_key}`. A\n  transfer's key stays bound to that transfer **forever**: it cannot be reused even after the 35 days, and the transfer\n  can be found with `GET /v1/outgoing_transfers?idempotency_key=…`.\n- If the first response was lost (for example, a dropped connection), the retry returns the **current state** of the\n  same transfer, which may be further along than the original.\n- A `202` response is stored and replayed like any `2xx`. The current state is available at\n  `GET /v1/outgoing_transfers/{id}` or arrives by webhook.\n\n## Versioning\n\n- The major version goes in the path (`/v1`) and changes only with a complete redesign.\n- The date-based version goes in `FCM-Version: YYYY-MM-DD`. Each entity is pinned to the version that is current when it\n  is onboarded and can try another one by sending the header. The response returns the version used.\n- Additive changes ship without a new version: new endpoints, new optional fields in requests, new fields in responses,\n  new values in response enums and new event types. **Your integration must ignore unknown fields and event types and\n  tolerate new enum values.**\n- Breaking changes only ship in a new version. A version is maintained for at least 12 months after the next one is\n  published, and its retirement is announced 6 months in advance (`Deprecation` and `Sunset` headers, an email to the\n  technical contact and the changelog). At most two versions are live at the same time.\n- Webhooks and events are built in the entity's pinned version (`api_version` field).\n\n## Amounts, currency and dates\n\n- Amounts: **integers in cents** of Argentine pesos (`150000` = ARS 1,500.00). Currency: `ARS` only.\n- Dates and times: RFC 3339 in UTC. The business day and the daily and monthly limits use the Buenos Aires calendar\n  (`America/Argentina/Buenos_Aires`).\n\n## Pagination\n\nLists return newest first. `limit` goes from 1 to 100 (25 by default); for the next page, send `starting_after` with\nthe `next_cursor` you received. Events are different: ascending order by `sequence` (see \"Events and webhooks\").\n\n## Errors\n\nEvery error has the same shape:\n\n```json\n{\n  \"error\": {\n    \"type\": \"transfer_declined\",\n    \"code\": \"insufficient_funds\",\n    \"message\": \"La cuenta acc_01JA2B3C4D5E6F7G8H9JKMNPQR tiene 120000 centavos disponibles y la transferencia necesita 150050.\",\n    \"display_message\": \"No hay saldo suficiente para esta transferencia.\",\n    \"param\": \"amount\",\n    \"request_id\": \"req_01JA2B3C4D5E6F7G8H9JKMNPQT\",\n    \"retryable\": false,\n    \"doc_url\": \"https://docs.fcmpayments.com/errores#insufficient_funds\",\n    \"transfer_id\": \"otr_01JA2B3C4D5E6F7G8H9JKMNPQV\"\n  }\n}\n```\n\n`message` is for the integrator; `display_message` is a text in Spanish suitable for showing to an end user (or\n`null`). `retryable: true` means: retry the same request with the same `Idempotency-Key`.\n\n| `code` | HTTP | Retryable | ISO 20022 (reason) |\n|---|---|---|---|\n| `invalid_request` | 400 | no | |\n| `parameter_missing` | 400 | no | |\n| `parameter_invalid` | 400 | no | |\n| `amount_invalid` | 400 | no | AM12 |\n| `currency_not_supported` | 400 | no | AM03 |\n| `concept_not_supported` | 400 | no | |\n| `destination_invalid` | 400 | no | AC01 |\n| `unsupported_version` | 400 | no | |\n| `idempotency_key_missing` | 400 | no | |\n| `idempotency_key_invalid` | 400 | no | |\n| `api_key_missing` | 401 | no | |\n| `api_key_invalid` | 401 | no | |\n| `signature_missing` | 401 | no | |\n| `signature_invalid` | 401 | no | |\n| `signature_expired` | 401 | yes, with a new signature | |\n| `signature_replayed` | 401 | no | |\n| `signature_key_unknown` | 401 | no | |\n| `content_digest_mismatch` | 401 | no | |\n| `ip_not_allowed` | 403 | no | |\n| `environment_mismatch` | 403 | no | |\n| `entity_suspended` | 403 | no | |\n| `not_found` | 404 | no | |\n| `idempotency_key_reused` | 409 | no | |\n| `idempotency_request_in_progress` | 409 | yes | |\n| `insufficient_funds` | 422 | no | AM04 |\n| `account_frozen` | 422 | no | AC06 |\n| `account_closed` | 422 | no | AC04 |\n| `limit_per_transfer_exceeded` | 422 | no | AM02 |\n| `limit_daily_exceeded` | 422 | no | AM14 |\n| `limit_monthly_exceeded` | 422 | no | AM14 |\n| `destination_not_found` | 422 | no | AC03 |\n| `destination_inactive` | 422 | no | AC04 |\n| `destination_is_source` | 422 | no | AG01 |\n| `destination_holder_mismatch` | 422 | no | BE01 |\n| `compliance_rejected` | 422 | no | RR04 |\n| `risk_rejected` | 422 | no | AG01 |\n| `rate_limited` | 429 | yes | |\n| `internal_error` | 500 | yes | |\n| `rail_unavailable` | 503 | yes | |\n| `ledger_unavailable` | 503 | yes | |\n| `transfers_paused` | 503 | yes | |\n| `service_unavailable` | 503 | yes | |\n\n`compliance_rejected` and `risk_rejected` are deliberately generic: FCM Payment Solutions S.A.S. does not disclose the\ndetails of an anti-money-laundering control (Argentine Law 25,246, duty of non-disclosure).\nFCM Payment Solutions S.A.S. does keep the full reason.\n\n## Outgoing transfers: authorize before transferring\n\nSee `POST /v1/outgoing_transfers`. States and their ISO 20022 equivalent (pacs.002, `TransactionStatus`):\n\n| `status` | What it means | Ledger hold | ISO 20022 |\n|---|---|---|---|\n| `pending` | Grip Rails received the order but cannot confirm the authorization yet. Money may be on hold. Do not retry with another Idempotency-Key. | There may be a pending one | `PDNG` |\n| `rejected` | The authorization declined it. No money moved. | None, or released | `RJCT` |\n| `reserved` | Authorized; amount plus charges on hold; not yet sent to COELSA. | Pending | `ACCP` |\n| `submitted` | Sent to COELSA; the result is pending. With `status_reason: late_settlement_review`, reopened by a late settlement (see below). | Pending (reopened: released, with the amount already debited) | `ACSP` |\n| `completed` | COELSA credited it at the destination. | Posted | `ACCC` |\n| `failed` | COELSA rejected it or it expired. No charge. If COELSA credits it later, it is reopened and the amount is debited, always without a fee (see below). | Released | `RJCT` |\n\n**Late settlement.** If COELSA credits a transfer we already reported to you as `failed` (its notice arrived late or\nwas lost), the money left and Grip Rails reopens it: it goes back to `submitted` with `status_reason:\nlate_settlement_review` (ISO `ACSP`; `failure` and `failed_at` go back to `null`) and we notify you with the event\n`transfer.outgoing.reopened`. The amount is debited from your funds account with a compensating entry (its references\nare in the event); if the funds are not enough, your guarantee account covers the shortfall and the rest remains as a\ndebt to FCM Payment Solutions S.A.S., as set out in the contract. Grip Rails disables your outgoing transfers, and they\nstay disabled, even after `completed`, until Grip Rails enables them again (only in the sandbox are they re-enabled\nautomatically after the simulated closing: see Sandbox). When two people at Grip Rails confirm the settlement, the\ntransfer moves to `completed` (event `transfer.outgoing.completed`) **free of charge**: Grip Rails charges no fee for a\nreopened transfer, because the reopening is not your fault (Grip Rails only releases a hold with evidence). `charges`\nshows it with an `fcm_fee` charge of `0` and its reason, and `total_debit_amount` is the amount. It is the only way out\nof `failed`; do not repeat it with another Idempotency-Key.\n\nIncoming credits have a single state in phase 1, `posted`, equivalent to `BOOK` (camt.054).\n\n## Events and webhooks\n\n- **Thin webhooks:** the body carries the type and the id of the object, not the object. For the current state, fetch\n  the object with its `GET`.\n- **Cursor-based reconciliation:** `GET /v1/events?after_sequence=N`. `sequence` is contiguous per entity (1, 2, 3…):\n  if you received 41 and then 43, you are missing 42. Every money event carries `ledger_references`, the stable\n  reference to the ledger entries that produced it.\n- **At-least-once delivery.** A webhook can arrive more than once or out of order: deduplicate by the event `id` and\n  sort by `sequence`.\n- **Retries:** if your endpoint does not respond `2xx` within 10 s, we retry 24 more times (25 attempts) over about 4.7\n  days: 30 s, 1 min, 2 min, 5 min, 10 min, 15 min, 30 min, 1 h, 2 h, 3 h, 4 h, 5 h and then every 8 h, with ±20%\n  jitter. Events stay in `/v1/events` for 90 days.\n- **Asymmetric signature:** the `FCM-Signature` header is a compact JWS (JWT) signed by Grip Rails with an asymmetric\n  key kept in KMS (`EdDSA` over Ed25519 or `ES256` over P-256; the JWKS says which). Claims: `iss`\n  (`https://api.fcmpayments.com`), `aud` (your `entity_id`), `iat`, `exp` (`iat` + 300), `jti` (delivery id, unique),\n  `evt` (event id) and `body_sha256` (SHA-256 of the raw body bytes, base64url without padding).\n- **How to verify:** (1) read `kid` and `alg` from the JWS header and look up that key in the JWKS (cached; if the\n  `kid` is not there, refresh at most once per minute); (2) verify the signature with the `alg` the JWKS publishes for\n  that `kid`, never with the one the request states if they differ; (3) require `iat` within the last 5 minutes and an\n  `exp` in the future; (4) recompute `body_sha256` over the raw body; (5) reject a `jti` you have already seen (keep it\n  for 10 minutes); (6) require `aud` to equal your `entity_id`.\n- **JWKS:** `GET /.well-known/jwks.json`, no authentication, cacheable. A new key is published 7 days before it starts\n  being used, and the old one stays published for 7 days after it stops being used.\n- **Your endpoint:** public HTTPS with TLS 1.2 or higher, no redirects (we do not follow them), response within 10 s at\n  most. Process in the background. Grip Rails registers the URL at onboarding, and it is changed through a verified\n  request.\n\n## Sandbox\n\n- Outgoing transfers complete on their own after ~2 s, unless the cents of the amount say otherwise: `…13` ends `failed`\n  with `destination_closed` (COELSA 7502), `…70` ends `failed` with `rail_expired` (COELSA 7570) and `…99` stays\n  `submitted` until you resolve it with `POST /v1/simulations/outgoing_transfers/{id}/rail_result`.\n- Test aliases (the human-readable names of Argentine accounts) for validating counterparties: `sbx.valida` (valid),\n  `sbx.inactiva`, `sbx.inexistente`, `sbx.bloqueada` (compliance control) and `sbx.otro.titular` (a CUIT, the\n  Argentine tax ID, other than the expected one, if you send `expected_cuit`). Any CBU (bank account number) or CVU\n  (virtual account number) with correct check digits validates with a synthetic holder.\n- `POST /v1/simulations/incoming_transfers` simulates an incoming credit: it goes through the same path as a real\n  COELSA notice (ledger entry, fee, event and webhook).\n- Per-entity test clock: `GET /v1/simulations/clock` and `POST /v1/simulations/clock/advance` move the entity's time\n  forward (daily and monthly limits, webhook retries, expirations).\n- Late settlement, in two steps: on a `failed` transfer that went out through COELSA (for example, a `…13`),\n  `POST /v1/simulations/outgoing_transfers/{id}/rail_result` with `{\"outcome\":\"completed\"}` simulates that COELSA\n  credited it later, through the same path as in production (compensating debit of the amount, outgoing transfers\n  disabled, `submitted` with `late_settlement_review` and the event `transfer.outgoing.reopened`). Then,\n  `POST /v1/simulations/outgoing_transfers/{id}/late_settlement_review` with `{\"outcome\":\"completed\"}` simulates that\n  two people at Grip Rails confirm the settlement: a few seconds later Grip Rails closes it as `completed`, free of\n  charge, with the event `transfer.outgoing.completed`. As in production, from the first step on your outgoing\n  transfers are disabled (`403 entity_suspended`). **In the sandbox only**, a few seconds after that\n  `transfer.outgoing.completed` Grip Rails enables them again on its own (`GET /v1/entity` is back to `active`; not if\n  Grip Rails blocked your entity in the meantime) and you can keep testing; in production they stay disabled until Grip\n  Rails re-enables them.\n\n## Rate limits\n\nPer entity: 5 writes and 25 reads per second (can be raised by contract). Above that: `429 rate_limited` with\n`Retry-After`.\n",
    "license": {
      "name": "La tecnología de Grip Rails es de Peppe IO, LLC (Grip). En la Argentina la opera FCM Payment Solutions S.A.S. El uso de la API se rige por el contrato entre FCM Payment Solutions S.A.S. y cada cliente."
    },
    "contact": {
      "name": "FCM Payment Solutions S.A.S.",
      "url": "https://fcmpayments.com"
    }
  },
  "servers": [
    {
      "url": "https://api.fcmpayments.com",
      "description": "Production. Real money; transfers through COELSA are irreversible."
    },
    {
      "url": "https://empresas-sandbox.fcmpayments.com",
      "description": "Sandbox. Simulated ledger and rail; it never moves real money."
    }
  ],
  "security": [
    {
      "apiKey": [],
      "requestSignature": []
    }
  ],
  "tags": [
    {
      "name": "Entidad",
      "description": "The authenticated business client, its limits, its usage and its fees."
    },
    {
      "name": "Cuentas",
      "description": "The entity's payment accounts in pesos (funds and guarantee), with their ledger balance."
    },
    {
      "name": "Números de cuenta",
      "description": "The CVUs of each account (N per account). The entity is the holder of all of them."
    },
    {
      "name": "Contrapartes",
      "description": "Validation of a destination's holder against COELSA before paying."
    },
    {
      "name": "Transferencias salientes",
      "description": "Payments in pesos to a CBU, CVU or alias, with prior authorization and a ledger hold."
    },
    {
      "name": "Transferencias entrantes",
      "description": "Credits received on the entity's CVUs, posted with real settlement from COELSA."
    },
    {
      "name": "Eventos",
      "description": "An ordered record of what happened, for reconciliation and for webhooks."
    },
    {
      "name": "Idempotencia",
      "description": "Lookup of a used Idempotency-Key."
    },
    {
      "name": "Claves de FCM",
      "description": "Public keys with which Grip Rails signs webhooks."
    },
    {
      "name": "Sandbox",
      "description": "Simulators. They exist only in the sandbox."
    }
  ],
  "paths": {
    "/v1/entity": {
      "get": {
        "operationId": "getEntity",
        "tags": [
          "Entidad"
        ],
        "summary": "Get the authenticated entity",
        "description": "Returns the entity that owns the API key, with its limits, its usage for the day and the month, and its fees.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The entity.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entity"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/accounts": {
      "get": {
        "operationId": "listAccounts",
        "tags": [
          "Cuentas"
        ],
        "summary": "List accounts",
        "description": "Lists the entity's accounts with their balance and status.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of accounts.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/accounts/{account_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountIdPath"
        }
      ],
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Cuentas"
        ],
        "summary": "Get an account (balance and status)",
        "description": "Balance and status of an account. The balance comes from the ledger at the time of the request:\n`posted` is what has been posted, `held` what is on hold for outgoing transfers in progress, and `available` = `posted` − `held`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "examples": {
                  "fondos": {
                    "summary": "Funds account with an outgoing transfer in progress",
                    "value": {
                      "id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "object": "account",
                      "entity_id": "ent_01JA2B3C4D5E6F7G8H9JKMNPQA",
                      "type": "funds",
                      "name": "Fondos operativos",
                      "currency": "ARS",
                      "status": "active",
                      "balances": {
                        "posted": 2500000,
                        "held": 150050,
                        "available": 2349950
                      },
                      "primary_account_number_id": "acn_01JA2B3C4D5E6F7G8H9JKMNPQB",
                      "created_at": "2026-10-15T13:00:00Z",
                      "updated_at": "2026-10-16T18:22:41Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/account_numbers": {
      "get": {
        "operationId": "listAccountNumbers",
        "tags": [
          "Números de cuenta"
        ],
        "summary": "List account numbers (CVUs)",
        "description": "Lists the entity's CVUs. An account can have N CVUs (for example, one for each of the client's users or merchants, to\nattribute collections); the entity is the holder of all of them, and whatever comes in through any of them is posted to\nthe same account. In phase 1 Grip Rails creates them.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/AccountIdQuery"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filters by status.",
            "schema": {
              "$ref": "#/components/schemas/AccountNumberStatus"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of account numbers.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountNumberList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/account_numbers/{account_number_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountNumberIdPath"
        }
      ],
      "get": {
        "operationId": "getAccountNumber",
        "tags": [
          "Números de cuenta"
        ],
        "summary": "Get an account number",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The account number.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountNumber"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/counterparty_validations": {
      "post": {
        "operationId": "createCounterpartyValidation",
        "tags": [
          "Contrapartes"
        ],
        "summary": "Validate a counterparty",
        "description": "Asks COELSA who the holder of a CBU, CVU or alias is, whether the account is active and whether it passes Grip Rails'\ncompliance controls. It does not move money.\n\nA `valid` validation lasts 15 minutes (`expires_at`) and can be passed in `counterparty_validation_id` when creating the\ntransfer so it is not looked up again. A negative result is not an HTTP error: it responds `201` with the corresponding\n`result`. If COELSA does not answer: `503 rail_unavailable`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CounterpartyValidationRequest"
              },
              "examples": {
                "alias": {
                  "summary": "Validate an alias expecting a CUIT",
                  "value": {
                    "destination": {
                      "type": "alias",
                      "value": "proveedor.ejemplo"
                    },
                    "expected_cuit": "27000000006"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Validation done (the result may be negative).",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CounterpartyValidation"
                },
                "examples": {
                  "valida": {
                    "summary": "Holder found and active",
                    "value": {
                      "id": "cval_01JA2B3C4D5E6F7G8H9JKMNPQC",
                      "object": "counterparty_validation",
                      "destination": {
                        "type": "alias",
                        "value": "proveedor.ejemplo"
                      },
                      "expected_cuit": "27000000006",
                      "result": "valid",
                      "display_message": null,
                      "counterparty": {
                        "name": "PROVEEDOR EJEMPLO",
                        "cuit": "27000000006",
                        "account": "0110599520000001234569",
                        "account_type": "cbu",
                        "bank_code": "011",
                        "alias": "proveedor.ejemplo",
                        "is_fcm_account": false
                      },
                      "validated_at": "2026-10-16T18:20:03Z",
                      "expires_at": "2026-10-16T18:35:03Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/outgoing_transfers": {
      "post": {
        "operationId": "createOutgoingTransfer",
        "tags": [
          "Transferencias salientes"
        ],
        "summary": "Create an outgoing transfer",
        "description": "Orders a transfer in pesos from one of the entity's accounts to a CBU, CVU or alias.\n\n**Before moving a single peso, Grip Rails authorizes**, in this order:\n\n1. Account and entity active, account not frozen and transfers enabled.\n2. Per-transaction limit.\n3. **Hold** in the ledger (pending transfer) for the amount plus charges. Without an available\n   balance: `insufficient_funds`.\n4. Destination holder according to COELSA (or a valid validation passed in `counterparty_validation_id`): it exists,\n   is active, is not the same account and, if you sent `expected_counterparty_cuit`, matches.\n5. Sanctions lists (Argentina's RePET, the UN and OFAC) on the destination holder.\n6. The entity's daily and monthly limits (transfers in `reserved`, `submitted` and `completed` count).\n7. Anti-fraud rules.\n\n**If everything passes:** `201` with the transfer in `reserved` and the event `transfer.outgoing.reserved`. Grip Rails\nsends it to COELSA right away (`submitted`) and closes it with COELSA's notice: `completed` (the money arrived and the\nhold is posted, with the event `transfer.outgoing.completed`) or `failed` (the hold is released, there is no charge,\nevent `transfer.outgoing.failed`). If COELSA credits it after reporting it as `failed`, Grip Rails reopens it\n(`submitted` with `status_reason: late_settlement_review`, event `transfer.outgoing.reopened`) and closes it as\n`completed`: see \"Late settlement\" in the general description.\n\n**If Grip Rails cannot confirm the result right away:** `202` with the transfer in `pending`, or in `submitted` with\n`status_reason: rail_outcome_unknown` if a retry finds it already sent and without a result. The order exists and is\nbound to your Idempotency-Key: do not repeat it with another key. A `pending` transfer ends in `reserved` (event\n`transfer.outgoing.reserved`) or in `failed` with `failure.code: canceled_by_fcm` (event `transfer.outgoing.failed`).\n\n**If something fails:** `422` with the reason in `error.code`. The transfer is stored as `rejected` (its id is in\n`error.transfer_id`) and the hold, if it was taken, is released. Malformed requests (`400`) create nothing.\n\n**COELSA is irreversible:** Grip Rails does not release a hold that was already sent without evidence of a rejection. If\nthe result does not arrive, the transfer stays `submitted` with `status_reason: rail_outcome_unknown` and the money stays\non hold until it is resolved. It is never resent with another identifier.\n\nIf the destination is a Grip Rails account, the transfer is internal (`rail.name: fcm_book`) and completes immediately.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OutgoingTransferCreateRequest"
              },
              "examples": {
                "cbu": {
                  "summary": "Payment to a CBU with a holder check",
                  "value": {
                    "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                    "amount": 150000,
                    "currency": "ARS",
                    "destination": {
                      "type": "cbu",
                      "value": "0110599520000001234569"
                    },
                    "expected_counterparty_cuit": "27000000006",
                    "concept": "FAC",
                    "description": "Factura 0003-00001234",
                    "external_reference": "pedido-7781",
                    "metadata": {
                      "agent_id": "agt_42"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Authorized and on hold.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransfer"
                },
                "examples": {
                  "reservada": {
                    "summary": "Just authorized",
                    "value": {
                      "id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV",
                      "object": "outgoing_transfer",
                      "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "account_number_id": "acn_01JA2B3C4D5E6F7G8H9JKMNPQB",
                      "amount": 150000,
                      "currency": "ARS",
                      "charges": [
                        {
                          "type": "fee",
                          "code": "fcm_fee",
                          "amount": 50,
                          "description": "Comisión por transferencia saliente"
                        }
                      ],
                      "total_debit_amount": 150050,
                      "destination": {
                        "type": "cbu",
                        "value": "0110599520000001234569"
                      },
                      "counterparty": {
                        "name": "PROVEEDOR EJEMPLO",
                        "cuit": "27000000006",
                        "account": "0110599520000001234569",
                        "account_type": "cbu",
                        "bank_code": "011",
                        "alias": null,
                        "is_fcm_account": false
                      },
                      "concept": "FAC",
                      "description": "Factura 0003-00001234",
                      "external_reference": "pedido-7781",
                      "metadata": {
                        "agent_id": "agt_42"
                      },
                      "status": "reserved",
                      "status_reason": null,
                      "iso20022": {
                        "status": "ACCP",
                        "reason": null
                      },
                      "authorization": {
                        "decision": "approved",
                        "decided_at": "2026-10-16T18:22:41Z",
                        "rationale": {
                          "code": "approved",
                          "message": "Pasó todos los controles."
                        },
                        "checks": [
                          {
                            "name": "account",
                            "result": "passed"
                          },
                          {
                            "name": "limits",
                            "result": "passed"
                          },
                          {
                            "name": "funds",
                            "result": "passed"
                          },
                          {
                            "name": "counterparty",
                            "result": "passed"
                          },
                          {
                            "name": "compliance",
                            "result": "passed"
                          }
                        ]
                      },
                      "reservation": {
                        "amount": 150050,
                        "state": "held",
                        "placed_at": "2026-10-16T18:22:41Z",
                        "resolved_at": null,
                        "ledger_references": [
                          "7f3a9c2e4b1d8f6a0c5e3b9d7a1f4c2e",
                          "1b8e4d2c9a7f3e5d0b6c8a2f4e1d9c7b"
                        ]
                      },
                      "rail": null,
                      "failure": null,
                      "idempotency_key": "01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "created_at": "2026-10-16T18:22:40Z",
                      "updated_at": "2026-10-16T18:22:41Z",
                      "completed_at": null,
                      "failed_at": null
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Received; the result cannot be confirmed yet. Do not retry with another Idempotency-Key.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/TransferDeclined"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "get": {
        "operationId": "listOutgoingTransfers",
        "tags": [
          "Transferencias salientes"
        ],
        "summary": "List outgoing transfers",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/AccountIdQuery"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filters by status.",
            "schema": {
              "$ref": "#/components/schemas/OutgoingTransferStatus"
            }
          },
          {
            "name": "idempotency_key",
            "in": "query",
            "required": false,
            "description": "Finds the transfer created with this Idempotency-Key (it also works after the 35 days).",
            "schema": {
              "$ref": "#/components/schemas/IdempotencyKeyValue"
            }
          },
          {
            "name": "external_reference",
            "in": "query",
            "required": false,
            "description": "Filters by your reference.",
            "schema": {
              "$ref": "#/components/schemas/ExternalReference"
            }
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of outgoing transfers.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransferList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/outgoing_transfers/{outgoing_transfer_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OutgoingTransferIdPath"
        }
      ],
      "get": {
        "operationId": "getOutgoingTransfer",
        "tags": [
          "Transferencias salientes"
        ],
        "summary": "Get an outgoing transfer",
        "description": "Current state of the transfer. It is what you should look up when you receive a webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The transfer.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransfer"
                },
                "examples": {
                  "completada": {
                    "summary": "Credited by COELSA",
                    "value": {
                      "id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV",
                      "object": "outgoing_transfer",
                      "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "account_number_id": "acn_01JA2B3C4D5E6F7G8H9JKMNPQB",
                      "amount": 150000,
                      "currency": "ARS",
                      "charges": [
                        {
                          "type": "fee",
                          "code": "fcm_fee",
                          "amount": 50,
                          "description": "Comisión por transferencia saliente"
                        }
                      ],
                      "total_debit_amount": 150050,
                      "destination": {
                        "type": "cbu",
                        "value": "0110599520000001234569"
                      },
                      "counterparty": {
                        "name": "PROVEEDOR EJEMPLO",
                        "cuit": "27000000006",
                        "account": "0110599520000001234569",
                        "account_type": "cbu",
                        "bank_code": "011",
                        "alias": null,
                        "is_fcm_account": false
                      },
                      "concept": "FAC",
                      "description": "Factura 0003-00001234",
                      "external_reference": "pedido-7781",
                      "metadata": {
                        "agent_id": "agt_42"
                      },
                      "status": "completed",
                      "status_reason": null,
                      "iso20022": {
                        "status": "ACCC",
                        "reason": null
                      },
                      "authorization": {
                        "decision": "approved",
                        "decided_at": "2026-10-16T18:22:41Z",
                        "rationale": {
                          "code": "approved",
                          "message": "Pasó todos los controles."
                        },
                        "checks": [
                          {
                            "name": "account",
                            "result": "passed"
                          },
                          {
                            "name": "limits",
                            "result": "passed"
                          },
                          {
                            "name": "funds",
                            "result": "passed"
                          },
                          {
                            "name": "counterparty",
                            "result": "passed"
                          },
                          {
                            "name": "compliance",
                            "result": "passed"
                          }
                        ]
                      },
                      "reservation": {
                        "amount": 150050,
                        "state": "posted",
                        "placed_at": "2026-10-16T18:22:41Z",
                        "resolved_at": "2026-10-16T18:22:44Z",
                        "ledger_references": [
                          "7f3a9c2e4b1d8f6a0c5e3b9d7a1f4c2e",
                          "1b8e4d2c9a7f3e5d0b6c8a2f4e1d9c7b"
                        ]
                      },
                      "rail": {
                        "name": "coelsa",
                        "operation_id": "86VRPQ2GX0O4XQ4Y9GLY0X",
                        "origin_reference": "9000000000017",
                        "status_code": "7500",
                        "submitted_at": "2026-10-16T18:22:42Z",
                        "finalized_at": "2026-10-16T18:22:44Z"
                      },
                      "failure": null,
                      "idempotency_key": "01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "created_at": "2026-10-16T18:22:40Z",
                      "updated_at": "2026-10-16T18:22:44Z",
                      "completed_at": "2026-10-16T18:22:44Z",
                      "failed_at": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/incoming_transfers": {
      "get": {
        "operationId": "listIncomingTransfers",
        "tags": [
          "Transferencias entrantes"
        ],
        "summary": "List incoming credits",
        "description": "Credits received on the entity's CVUs. Each one comes from a verified COELSA notice (a credit already settled in the\ncollection account of FCM Payment Solutions S.A.S.) and is posted to the ledger with its fee as a separate entry.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/AccountIdQuery"
          },
          {
            "name": "account_number_id",
            "in": "query",
            "required": false,
            "description": "Filters by the CVU it came in through.",
            "schema": {
              "$ref": "#/components/schemas/AccountNumberId"
            }
          },
          {
            "$ref": "#/components/parameters/CreatedAfter"
          },
          {
            "$ref": "#/components/parameters/CreatedBefore"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of incoming credits.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransferList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/incoming_transfers/{incoming_transfer_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/IncomingTransferIdPath"
        }
      ],
      "get": {
        "operationId": "getIncomingTransfer",
        "tags": [
          "Transferencias entrantes"
        ],
        "summary": "Get an incoming credit",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The incoming credit.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "examples": {
                  "credito": {
                    "summary": "Credit through a reference CVU",
                    "value": {
                      "id": "itr_01JA2B3C4D5E6F7G8H9JKMNPQW",
                      "object": "incoming_transfer",
                      "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "account_number_id": "acn_01JA2B3C4D5E6F7G8H9JKMNPQD",
                      "cvu": "0000214300000009999994",
                      "amount": 1500000,
                      "currency": "ARS",
                      "charges": [
                        {
                          "type": "fee",
                          "code": "fcm_fee",
                          "amount": 50,
                          "description": "Comisión por crédito entrante"
                        }
                      ],
                      "net_amount": 1499950,
                      "payer": {
                        "name": "PAGADOR EJEMPLO",
                        "cuit": "20000000001",
                        "account": "0000999100000000000420",
                        "account_type": "cvu",
                        "bank_code": null
                      },
                      "concept": "VAR",
                      "status": "posted",
                      "iso20022": {
                        "entry_status": "BOOK"
                      },
                      "rail": {
                        "name": "coelsa",
                        "operation_id": "WY7ZEPN6YGGPE6LV2Q0M5X",
                        "business_date": "2026-10-16"
                      },
                      "ledger_references": [
                        "3c9e1a7b5d2f8e4c6a0b9d3f7e1c5a2b",
                        "9d4b2f8a6c1e7d3b5f0a8c2e6d4b1f9a",
                        "5e2c8a4f1b7d9e3c6a0f2b8d4e6c1a7f"
                      ],
                      "posted_at": "2026-10-16T21:20:15Z",
                      "created_at": "2026-10-16T21:20:15Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Eventos"
        ],
        "summary": "List events by cursor",
        "description": "The entity's events in ascending order of `sequence`, starting after `after_sequence`. Without a type filter,\n`sequence` is contiguous: store the last one you processed and fetch from there. With the `type` filter the sequence has\ngaps by design (it is only for lookups). It is the source for reconciliation; webhooks are a notice.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "name": "after_sequence",
            "in": "query",
            "required": false,
            "description": "Returns the events with a `sequence` greater than this value. 0 by default (from the first one available).",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filters by event type.",
            "schema": {
              "$ref": "#/components/schemas/EventType"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of events (1 to 100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of events.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                },
                "examples": {
                  "pagina": {
                    "summary": "Two events",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "id": "evt_01JA2B3C4D5E6F7G8H9JKMNPQX",
                          "object": "event",
                          "type": "transfer.outgoing.reserved",
                          "sequence": 41,
                          "entity_id": "ent_01JA2B3C4D5E6F7G8H9JKMNPQA",
                          "api_version": "2026-10-15",
                          "created_at": "2026-10-16T18:22:41Z",
                          "data": {
                            "object": "outgoing_transfer",
                            "id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV",
                            "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                            "ledger_references": [
                              "7f3a9c2e4b1d8f6a0c5e3b9d7a1f4c2e",
                              "1b8e4d2c9a7f3e5d0b6c8a2f4e1d9c7b"
                            ]
                          }
                        },
                        {
                          "id": "evt_01JA2B3C4D5E6F7G8H9JKMNPQY",
                          "object": "event",
                          "type": "transfer.outgoing.completed",
                          "sequence": 42,
                          "entity_id": "ent_01JA2B3C4D5E6F7G8H9JKMNPQA",
                          "api_version": "2026-10-15",
                          "created_at": "2026-10-16T18:22:44Z",
                          "data": {
                            "object": "outgoing_transfer",
                            "id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV",
                            "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                            "ledger_references": [
                              "2a6d9f3c8e1b5a7d4c0e9b2f6a8d3c1e",
                              "8c3f7a1e5d9b2c6f0a4e8d1b7c5f3a9e"
                            ]
                          }
                        }
                      ],
                      "has_more": false,
                      "next_after_sequence": 42
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/events/{event_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EventIdPath"
        }
      ],
      "get": {
        "operationId": "getEvent",
        "tags": [
          "Eventos"
        ],
        "summary": "Get an event",
        "description": "Useful for confirming a received webhook through another channel.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/idempotency_keys/{idempotency_key}": {
      "parameters": [
        {
          "name": "idempotency_key",
          "in": "path",
          "required": true,
          "description": "The Idempotency-Key that was used.",
          "schema": {
            "$ref": "#/components/schemas/IdempotencyKeyValue"
          }
        }
      ],
      "get": {
        "operationId": "getIdempotencyKey",
        "tags": [
          "Idempotencia"
        ],
        "summary": "Look up an Idempotency-Key",
        "description": "Says whether the key is in progress or finished, which request it was used with (fingerprint), what we responded and\nwhich object it created. Available for 35 days from first use. Transfer keys stay bound to their transfer after that\nperiod (see `GET /v1/outgoing_transfers?idempotency_key=`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The key's record.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "FCM-Version": {
                "$ref": "#/components/headers/FcmVersionUsed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdempotencyRecord"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "operationId": "getJwks",
        "tags": [
          "Claves de FCM"
        ],
        "summary": "Grip Rails public keys for verifying webhooks",
        "description": "JWKS (RFC 7517) with the current public keys with which Grip Rails signs webhooks. No authentication required.\nThe private keys live in KMS and never leave it.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Key set.",
            "headers": {
              "Cache-Control": {
                "description": "Can be cached for 5 minutes.",
                "schema": {
                  "type": "string",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Jwks"
                },
                "examples": {
                  "p256": {
                    "summary": "A current P-256 key",
                    "value": {
                      "keys": [
                        {
                          "kty": "EC",
                          "crv": "P-256",
                          "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
                          "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
                          "kid": "fcm-whk-2026-10",
                          "alg": "ES256",
                          "use": "sig"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/simulations/incoming_transfers": {
      "post": {
        "operationId": "simulateIncomingTransfer",
        "tags": [
          "Sandbox"
        ],
        "summary": "Simulate an incoming credit",
        "description": "Builds a synthetic COELSA notice to one of the entity's CVUs and runs it through the same path as a real one: an entry in\nthe sandbox ledger, the fee, the event `transfer.incoming.posted` and the webhook. Sandbox only.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateIncomingTransferRequest"
              },
              "examples": {
                "basico": {
                  "summary": "ARS 15,000 credit",
                  "value": {
                    "account_number_id": "acn_01JA2B3C4D5E6F7G8H9JKMNPQD",
                    "amount": 1500000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Credit simulated and posted.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/simulations/outgoing_transfers/{outgoing_transfer_id}/rail_result": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OutgoingTransferIdPath"
        }
      ],
      "post": {
        "operationId": "simulateOutgoingTransferRailResult",
        "tags": [
          "Sandbox"
        ],
        "summary": "Simulate COELSA's result for a transfer",
        "description": "Injects COELSA's result notice (equivalent to `AvisoOperacionFinalizada`) for a transfer in `submitted`: `completed`\nposts the hold and `failed` releases it. On a `failed` transfer that went out through COELSA, `completed` simulates a\nlate settlement: the notice arrives after the hold was released and Grip Rails reopens the transfer through the same\npath as in production (compensating debit of the amount, outgoing transfers disabled, `submitted` with `status_reason:\nlate_settlement_review` and the event `transfer.outgoing.reopened`). Sandbox only. Any other case responds\n`400 parameter_invalid`: another state, a `failed` transfer with `outcome: failed`, a `failed` transfer that never went\nout to COELSA, or a reopened one (Grip Rails closes it: see\n`POST /v1/simulations/outgoing_transfers/{id}/late_settlement_review`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateRailResultRequest"
              },
              "examples": {
                "rechazo": {
                  "summary": "COELSA rejects because the account does not exist",
                  "value": {
                    "outcome": "failed",
                    "coelsa_code": "7502"
                  }
                },
                "acreditacion_tardia": {
                  "summary": "On a failed transfer, COELSA credits it later (late settlement)",
                  "value": {
                    "outcome": "completed"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result applied. Returns the updated transfer.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/simulations/outgoing_transfers/{outgoing_transfer_id}/late_settlement_review": {
      "parameters": [
        {
          "$ref": "#/components/parameters/OutgoingTransferIdPath"
        }
      ],
      "post": {
        "operationId": "simulateOutgoingTransferLateSettlementReview",
        "tags": [
          "Sandbox"
        ],
        "summary": "Simulate that two people at Grip Rails confirm a late settlement",
        "description": "For a transfer reopened by a late settlement (`submitted` with `status_reason: late_settlement_review`), simulates that\ntwo people at Grip Rails confirm the settlement: in the sandbox they are two different synthetic operators who propose\nand approve the reconciliation through the same path as in production. The `202` response carries the transfer still\nreopened; a few seconds later Grip Rails closes it as `completed`, free of charge (`charges` with an `fcm_fee` of `0`\nand its reason), with the event `transfer.outgoing.completed`, and a few seconds after that it enables your outgoing\ntransfers again (`GET /v1/entity` is back to `active`; not if Grip Rails blocked your entity in the meantime): that\nautomatic re-enabling exists only in the sandbox; in production they stay disabled until Grip Rails re-enables them.\nSandbox only. A transfer that is not reopened responds `400 parameter_invalid`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateLateSettlementReviewRequest"
              },
              "examples": {
                "confirmada": {
                  "summary": "Two people at Grip Rails confirm the late settlement",
                  "value": {
                    "outcome": "completed"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Confirmed by two synthetic people. Returns the transfer still reopened; Grip Rails closes it as `completed` within seconds.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutgoingTransfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/simulations/clock": {
      "get": {
        "operationId": "getSandboxClock",
        "tags": [
          "Sandbox"
        ],
        "summary": "Get the entity's test clock",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          }
        ],
        "responses": {
          "200": {
            "description": "The clock.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxClock"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/simulations/clock/advance": {
      "post": {
        "operationId": "advanceSandboxClock",
        "tags": [
          "Sandbox"
        ],
        "summary": "Advance the test clock",
        "description": "Moves the entity's time in the sandbox forward (never back). It affects the day and month of the limits, pending\nwebhook retries and simulated expirations. Every date the sandbox returns for that entity uses this clock.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FcmVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/SignatureInput"
          },
          {
            "$ref": "#/components/parameters/ContentDigest"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdvanceClockRequest"
              },
              "examples": {
                "un_dia": {
                  "summary": "Advance one day",
                  "value": {
                    "advance_seconds": 86400
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Clock advanced.",
            "headers": {
              "FCM-Request-Id": {
                "$ref": "#/components/headers/FcmRequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxClock"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "webhooks": {
    "transfer.incoming.posted": {
      "post": {
        "operationId": "webhookTransferIncomingPosted",
        "tags": [
          "Eventos"
        ],
        "summary": "Money came into one of the entity's accounts",
        "description": "An incoming credit was posted. Call `GET /v1/incoming_transfers/{data.id}` for the details.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookEventType"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryId"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryAttempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              },
              "examples": {
                "credito": {
                  "value": {
                    "id": "evt_01JA2B3C4D5E6F7G8H9JKMNPQZ",
                    "object": "event",
                    "type": "transfer.incoming.posted",
                    "sequence": 43,
                    "entity_id": "ent_01JA2B3C4D5E6F7G8H9JKMNPQA",
                    "api_version": "2026-10-15",
                    "created_at": "2026-10-16T21:20:15Z",
                    "data": {
                      "object": "incoming_transfer",
                      "id": "itr_01JA2B3C4D5E6F7G8H9JKMNPQW",
                      "account_id": "acc_01JA2B3C4D5E6F7G8H9JKMNPQR",
                      "ledger_references": [
                        "3c9e1a7b5d2f8e4c6a0b9d3f7e1c5a2b",
                        "9d4b2f8a6c1e7d3b5f0a8c2e6d4b1f9a",
                        "5e2c8a4f1b7d9e3c6a0f2b8d4e6c1a7f"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Respond within 10 s at most and process in the background."
          },
          "default": {
            "description": "Cualquier otra respuesta, o ninguna en 10 s, dispara un reintento según la política."
          }
        }
      }
    },
    "transfer.outgoing.reserved": {
      "post": {
        "operationId": "webhookTransferOutgoingReserved",
        "tags": [
          "Eventos"
        ],
        "summary": "An outgoing transfer was authorized and put on hold",
        "description": "The authorization approved the transfer, and the amount plus charges are on hold in the ledger.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookEventType"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryId"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryAttempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received."
          },
          "default": {
            "description": "Reintento según la política."
          }
        }
      }
    },
    "transfer.outgoing.completed": {
      "post": {
        "operationId": "webhookTransferOutgoingCompleted",
        "tags": [
          "Eventos"
        ],
        "summary": "An outgoing transfer was credited",
        "description": "COELSA confirmed the credit at the destination and the hold was posted. It is irreversible. A transfer reopened by a\nlate settlement arrives here when Grip Rails closes it, free of charge: its only debit is the compensating entry (its\nreferences are in `data.ledger_references`).\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookEventType"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryId"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryAttempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received."
          },
          "default": {
            "description": "Reintento según la política."
          }
        }
      }
    },
    "transfer.outgoing.failed": {
      "post": {
        "operationId": "webhookTransferOutgoingFailed",
        "tags": [
          "Eventos"
        ],
        "summary": "An outgoing transfer failed",
        "description": "COELSA rejected it or it expired, or Grip Rails canceled it before sending it. The hold was released and there was no charge. The reason is in the object's `failure`. If COELSA credits it later, `transfer.outgoing.reopened` arrives.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookEventType"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryId"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryAttempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received."
          },
          "default": {
            "description": "Reintento según la política."
          }
        }
      }
    },
    "transfer.outgoing.reopened": {
      "post": {
        "operationId": "webhookTransferOutgoingReopened",
        "tags": [
          "Eventos"
        ],
        "summary": "A failed outgoing transfer was reopened",
        "description": "COELSA credited a transfer we had already reported to you as `failed` (late settlement): the money left. The transfer\ngoes back to `submitted` with `status_reason: late_settlement_review`, the amount was debited with a compensating entry\n(its references are in `data.ledger_references`) and Grip Rails closes it as `completed` (event\n`transfer.outgoing.completed`) when two people confirm the settlement. Call `GET /v1/outgoing_transfers/{data.id}` for\nthe current state.\n",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookEventId"
          },
          {
            "$ref": "#/components/parameters/WebhookEventType"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryId"
          },
          {
            "$ref": "#/components/parameters/WebhookDeliveryAttempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received."
          },
          "default": {
            "description": "Reintento según la política."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fcm_live_… o fcm_test_…",
        "description": "The entity's API key in `Authorization: Bearer`. It identifies the entity and the environment. Grip Rails stores it\nhashed, and it can be rotated without downtime (up to two active). It is not enough on its own: every request is also\nsigned (`requestSignature`).\n"
      },
      "requestSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "Signature",
        "description": "Signature of the request per RFC 9421 (HTTP Message Signatures) with the client's private key (`ed25519` or\n`ecdsa-p256-sha256`). It goes with `Signature-Input` and, if there is a body, with `Content-Digest`. Required components\nand parameters are in the general description (\"Authentication\"). Grip Rails only knows the public key: a leaked Grip\nRails database does not allow anyone to forge the client's instructions.\n"
      }
    },
    "parameters": {
      "FcmVersion": {
        "name": "FCM-Version",
        "in": "header",
        "required": false,
        "description": "Date-based version of the contract. If missing, the entity's pinned version is used.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "examples": [
            "2026-10-15"
          ]
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique key per client intent. Required on every POST. See \"Idempotency\".",
        "schema": {
          "$ref": "#/components/schemas/IdempotencyKeyValue"
        }
      },
      "SignatureInput": {
        "name": "Signature-Input",
        "in": "header",
        "required": true,
        "description": "RFC 9421 signature parameters (covered components, `created`, `nonce`, `keyid`, `alg`).",
        "schema": {
          "type": "string",
          "maxLength": 2048,
          "examples": [
            "sig1=(\"@method\" \"@target-uri\");created=1760112000;nonce=\"Vq3n8sZx0bR1yTqLkM2pWg\";keyid=\"ck_01JA2B3C4D5E6F7G8H9JKMNPQS\";alg=\"ed25519\""
          ]
        }
      },
      "ContentDigest": {
        "name": "Content-Digest",
        "in": "header",
        "required": true,
        "description": "SHA-256 of the exact bytes of the body, per RFC 9530.",
        "schema": {
          "type": "string",
          "pattern": "^sha-256=:[A-Za-z0-9+/]{43}=:$",
          "examples": [
            "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"
          ]
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of objects per page.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "StartingAfter": {
        "name": "starting_after",
        "in": "query",
        "required": false,
        "description": "Opaque cursor (`next_cursor` from the previous page).",
        "schema": {
          "type": "string",
          "maxLength": 200
        }
      },
      "CreatedAfter": {
        "name": "created_after",
        "in": "query",
        "required": false,
        "description": "Only objects created at or after this instant (RFC 3339).",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "CreatedBefore": {
        "name": "created_before",
        "in": "query",
        "required": false,
        "description": "Only objects created before this instant (RFC 3339).",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "AccountIdQuery": {
        "name": "account_id",
        "in": "query",
        "required": false,
        "description": "Filters by account.",
        "schema": {
          "$ref": "#/components/schemas/AccountId"
        }
      },
      "AccountIdPath": {
        "name": "account_id",
        "in": "path",
        "required": true,
        "description": "Id of the account.",
        "schema": {
          "$ref": "#/components/schemas/AccountId"
        }
      },
      "AccountNumberIdPath": {
        "name": "account_number_id",
        "in": "path",
        "required": true,
        "description": "Id of the account number.",
        "schema": {
          "$ref": "#/components/schemas/AccountNumberId"
        }
      },
      "OutgoingTransferIdPath": {
        "name": "outgoing_transfer_id",
        "in": "path",
        "required": true,
        "description": "Id of the outgoing transfer.",
        "schema": {
          "$ref": "#/components/schemas/OutgoingTransferId"
        }
      },
      "IncomingTransferIdPath": {
        "name": "incoming_transfer_id",
        "in": "path",
        "required": true,
        "description": "Id of the incoming credit.",
        "schema": {
          "$ref": "#/components/schemas/IncomingTransferId"
        }
      },
      "EventIdPath": {
        "name": "event_id",
        "in": "path",
        "required": true,
        "description": "Id of the event.",
        "schema": {
          "$ref": "#/components/schemas/EventId"
        }
      },
      "WebhookSignature": {
        "name": "FCM-Signature",
        "in": "header",
        "required": true,
        "description": "Compact JWS (JWT) signed by Grip Rails. See \"Events and webhooks\" for the claims and the verification steps.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$"
        }
      },
      "WebhookEventId": {
        "name": "FCM-Event-Id",
        "in": "header",
        "required": true,
        "description": "Id of the event (equal to the body's `id`). The key for deduplication.",
        "schema": {
          "$ref": "#/components/schemas/EventId"
        }
      },
      "WebhookEventType": {
        "name": "FCM-Event-Type",
        "in": "header",
        "required": true,
        "description": "Type of the event (equal to the body's `type`).",
        "schema": {
          "$ref": "#/components/schemas/EventType"
        }
      },
      "WebhookDeliveryId": {
        "name": "FCM-Delivery-Id",
        "in": "header",
        "required": true,
        "description": "Id of this delivery attempt (equal to the `jti` claim).",
        "schema": {
          "type": "string",
          "pattern": "^whd_[0-9A-HJKMNP-TV-Z]{26}$"
        }
      },
      "WebhookDeliveryAttempt": {
        "name": "FCM-Delivery-Attempt",
        "in": "header",
        "required": true,
        "description": "Attempt number (1 to 25).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 25
        }
      }
    },
    "headers": {
      "FcmRequestId": {
        "description": "Id of the request at Grip Rails. Quote it in any support inquiry.",
        "schema": {
          "$ref": "#/components/schemas/RequestId"
        }
      },
      "FcmVersionUsed": {
        "description": "Contract version used for the response.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        }
      },
      "IdempotentReplayed": {
        "description": "`true` if this is the stored response for that Idempotency-Key.",
        "schema": {
          "type": "boolean"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed request or invalid parameter. Nothing was created.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "parametro": {
                "value": {
                  "error": {
                    "type": "invalid_request_error",
                    "code": "amount_invalid",
                    "message": "amount tiene que ser un entero positivo en centavos.",
                    "display_message": null,
                    "param": "amount",
                    "request_id": "req_01JA2B3C4D5E6F7G8H9JKMNPQT",
                    "retryable": false,
                    "doc_url": "https://docs.fcmpayments.com/errores#amount_invalid",
                    "transfer_id": null
                  }
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key or the signature is missing, or they are not valid.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "firma": {
                "value": {
                  "error": {
                    "type": "authentication_error",
                    "code": "signature_expired",
                    "message": "El parámetro created de la firma está fuera de la ventana de ±300 segundos.",
                    "display_message": null,
                    "param": null,
                    "request_id": "req_01JA2B3C4D5E6F7G8H9JKMNPQT",
                    "retryable": true,
                    "doc_url": "https://docs.fcmpayments.com/errores#signature_expired",
                    "transfer_id": null
                  }
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential is valid but cannot do this (IP, environment or suspended entity).",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "It does not exist, or it belongs to another entity (Grip Rails does not distinguish between the two cases).",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency conflict.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "reusada": {
                "value": {
                  "error": {
                    "type": "idempotency_error",
                    "code": "idempotency_key_reused",
                    "message": "La Idempotency-Key 01JA2B3C4D5E6F7G8H9JKMNPQR ya se usó con otro pedido (POST /v1/outgoing_transfers, huella distinta).",
                    "display_message": null,
                    "param": "Idempotency-Key",
                    "request_id": "req_01JA2B3C4D5E6F7G8H9JKMNPQT",
                    "retryable": false,
                    "doc_url": "https://docs.fcmpayments.com/errores#idempotency_key_reused",
                    "transfer_id": null
                  }
                }
              }
            }
          }
        }
      },
      "TransferDeclined": {
        "description": "The authorization declined the transfer. It was stored as `rejected` and no money moved.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          },
          "Idempotent-Replayed": {
            "$ref": "#/components/headers/IdempotentReplayed"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "sin_saldo": {
                "value": {
                  "error": {
                    "type": "transfer_declined",
                    "code": "insufficient_funds",
                    "message": "La cuenta acc_01JA2B3C4D5E6F7G8H9JKMNPQR tiene 120000 centavos disponibles y la transferencia necesita 150050.",
                    "display_message": "No hay saldo suficiente para esta transferencia.",
                    "param": "amount",
                    "request_id": "req_01JA2B3C4D5E6F7G8H9JKMNPQT",
                    "retryable": false,
                    "doc_url": "https://docs.fcmpayments.com/errores#insufficient_funds",
                    "transfer_id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV"
                  }
                }
              },
              "titular": {
                "value": {
                  "error": {
                    "type": "transfer_declined",
                    "code": "destination_holder_mismatch",
                    "message": "COELSA informa otro CUIT para el titular del destino.",
                    "display_message": "La cuenta de destino no es de quien esperabas.",
                    "param": "expected_counterparty_cuit",
                    "request_id": "req_01JA2B3C4D5E6F7G8H9JKMNPQT",
                    "retryable": false,
                    "doc_url": "https://docs.fcmpayments.com/errores#destination_holder_mismatch",
                    "transfer_id": "otr_01JA2B3C4D5E6F7G8H9JKMNPQV"
                  }
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "You exceeded the limit of requests per second.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "A Grip Rails error. Retry with the same Idempotency-Key.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "COELSA, the ledger or transfers are not available right now. Retry with the same Idempotency-Key.",
        "headers": {
          "FCM-Request-Id": {
            "$ref": "#/components/headers/FcmRequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "EntityId": {
        "type": "string",
        "pattern": "^ent_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "ent_01JA2B3C4D5E6F7G8H9JKMNPQA"
        ]
      },
      "AccountId": {
        "type": "string",
        "pattern": "^acc_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "acc_01JA2B3C4D5E6F7G8H9JKMNPQR"
        ]
      },
      "AccountNumberId": {
        "type": "string",
        "pattern": "^acn_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "acn_01JA2B3C4D5E6F7G8H9JKMNPQB"
        ]
      },
      "OutgoingTransferId": {
        "type": "string",
        "description": "Deterministic, derived from the entity and the Idempotency-Key, so a retry reaches the same object.",
        "pattern": "^otr_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "otr_01JA2B3C4D5E6F7G8H9JKMNPQV"
        ]
      },
      "IncomingTransferId": {
        "type": "string",
        "description": "Deterministic, derived from COELSA's id for the credit.",
        "pattern": "^itr_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "itr_01JA2B3C4D5E6F7G8H9JKMNPQW"
        ]
      },
      "CounterpartyValidationId": {
        "type": "string",
        "pattern": "^cval_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "cval_01JA2B3C4D5E6F7G8H9JKMNPQC"
        ]
      },
      "EventId": {
        "type": "string",
        "pattern": "^evt_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "evt_01JA2B3C4D5E6F7G8H9JKMNPQX"
        ]
      },
      "RequestId": {
        "type": "string",
        "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$",
        "examples": [
          "req_01JA2B3C4D5E6F7G8H9JKMNPQT"
        ]
      },
      "IdempotencyKeyValue": {
        "type": "string",
        "pattern": "^[A-Za-z0-9._:-]{16,128}$",
        "examples": [
          "01JA2B3C4D5E6F7G8H9JKMNPQR"
        ]
      },
      "Amount": {
        "type": "integer",
        "format": "int64",
        "minimum": 1,
        "maximum": 9007199254740991,
        "description": "Amount in cents of Argentine pesos. `150000` = ARS 1,500.00.",
        "examples": [
          150000
        ]
      },
      "AmountOrZero": {
        "type": "integer",
        "format": "int64",
        "minimum": 0,
        "maximum": 9007199254740991,
        "description": "Amount in cents of Argentine pesos; it can be zero."
      },
      "Currency": {
        "type": "string",
        "enum": [
          "ARS"
        ],
        "description": "The API's only currency."
      },
      "Cuit": {
        "type": "string",
        "pattern": "^\\d{11}$",
        "description": "CUIT or CUIL (Argentine tax or labor ID), 11 digits without hyphens.",
        "examples": [
          "27000000006"
        ]
      },
      "BankKey": {
        "type": "string",
        "pattern": "^\\d{22}$",
        "description": "A 22-digit CBU or CVU (Argentine bank or virtual account number) with valid check digits.",
        "examples": [
          "0110599520000001234569"
        ]
      },
      "Alias": {
        "type": "string",
        "pattern": "^[A-Za-z0-9.-]{6,20}$",
        "description": "Alias in the COELSA system (the human-readable name of a CBU or CVU).",
        "examples": [
          "proveedor.ejemplo"
        ]
      },
      "LedgerReference": {
        "type": "string",
        "pattern": "^[0-9a-f]{32}$",
        "description": "Opaque, stable reference to a ledger entry (128-bit id, in hexadecimal). It is derived from stable identifiers (COELSA id, entity and Idempotency-Key), so it does not change\nif the ledger is rebuilt.\n",
        "examples": [
          "7f3a9c2e4b1d8f6a0c5e3b9d7a1f4c2e"
        ]
      },
      "ExternalReference": {
        "type": "string",
        "pattern": "^[A-Za-z0-9._:-]{1,64}$",
        "description": "The client's reference for reconciliation. It does not have to be unique and it does not travel to COELSA.",
        "examples": [
          "pedido-7781"
        ]
      },
      "Metadata": {
        "type": "object",
        "description": "Up to 20 key-value pairs from the client. Do not include personal data.",
        "maxProperties": 20,
        "propertyNames": {
          "pattern": "^[a-z0-9_]{1,40}$"
        },
        "additionalProperties": {
          "type": "string",
          "maxLength": 500
        }
      },
      "Concept": {
        "type": "string",
        "description": "BCRA purpose code for the transfer, which travels to COELSA. Initial list; it can grow additively.\nVAR miscellaneous, ALQ rent, CUO installments, EXP building fees, FAC invoices, HAB salaries, HON professional fees,\nPRE loans, SEG insurance.\n",
        "enum": [
          "VAR",
          "ALQ",
          "CUO",
          "EXP",
          "FAC",
          "HAB",
          "HON",
          "PRE",
          "SEG"
        ],
        "default": "VAR"
      },
      "EntityStatus": {
        "type": "string",
        "description": "`suspended`: it can read but cannot order transfers.",
        "enum": [
          "active",
          "suspended",
          "closed"
        ]
      },
      "Entity": {
        "type": "object",
        "required": [
          "id",
          "object",
          "legal_name",
          "cuit",
          "status",
          "environment",
          "api_version",
          "limits",
          "usage",
          "fees",
          "created_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/EntityId"
          },
          "object": {
            "type": "string",
            "const": "entity"
          },
          "legal_name": {
            "type": "string",
            "maxLength": 200
          },
          "cuit": {
            "description": "CUIT under which the entity holds its CVUs with COELSA.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Cuit"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/EntityStatus"
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "api_version": {
            "type": "string",
            "description": "Contract version pinned for the entity.",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "limits": {
            "$ref": "#/components/schemas/Limits"
          },
          "usage": {
            "$ref": "#/components/schemas/LimitUsage"
          },
          "fees": {
            "$ref": "#/components/schemas/FeeSchedule"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Limits": {
        "type": "object",
        "description": "The entity's limits for outgoing transfers, set by contract (approved by Grip Rails; they cannot be changed through the\nAPI). They count the amount, without charges, of transfers in `reserved`, `submitted` and `completed`. `null` = no limit.\n",
        "required": [
          "per_transfer_max_amount",
          "daily_max_amount",
          "monthly_max_amount"
        ],
        "properties": {
          "per_transfer_max_amount": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1
          },
          "daily_max_amount": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1
          },
          "monthly_max_amount": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 1
          }
        }
      },
      "LimitUsage": {
        "type": "object",
        "required": [
          "business_date",
          "daily_used_amount",
          "daily_remaining_amount",
          "month",
          "monthly_used_amount",
          "monthly_remaining_amount"
        ],
        "properties": {
          "business_date": {
            "type": "string",
            "format": "date",
            "description": "Buenos Aires day to which the daily usage corresponds."
          },
          "daily_used_amount": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "daily_remaining_amount": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "`null` if there is no daily limit."
          },
          "month": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}$"
          },
          "monthly_used_amount": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "monthly_remaining_amount": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "`null` if there is no monthly limit."
          }
        }
      },
      "FeeRule": {
        "type": "object",
        "required": [
          "fixed_amount",
          "rate_bps"
        ],
        "properties": {
          "fixed_amount": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "rate_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "description": "Percentage in basis points of the amount (100 = 1%). The fee is fixed + percentage, rounded down to the cent."
          }
        }
      },
      "FeeSchedule": {
        "type": "object",
        "description": "Fee per movement, as set in the contract. It is charged as a separate ledger entry: on an outgoing transfer it is held\ntogether with the transfer and released if the transfer fails; on an incoming one it is deducted when posting and never\nexceeds the amount received.\n",
        "required": [
          "outgoing",
          "incoming"
        ],
        "properties": {
          "outgoing": {
            "$ref": "#/components/schemas/FeeRule"
          },
          "incoming": {
            "$ref": "#/components/schemas/FeeRule"
          }
        }
      },
      "AccountType": {
        "type": "string",
        "description": "`funds`: funds account, the one that operates. `guarantee`: guarantee account (a reserve) that the client deposits as\nset in the contract to cover shortfalls; it is visible but cannot be debited through the API.\n",
        "enum": [
          "funds",
          "guarantee"
        ]
      },
      "AccountStatus": {
        "type": "string",
        "description": "`frozen`: it cannot order outgoing transfers; incoming ones are posted.",
        "enum": [
          "active",
          "frozen",
          "closed"
        ]
      },
      "Balances": {
        "type": "object",
        "required": [
          "posted",
          "held",
          "available"
        ],
        "properties": {
          "posted": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "held": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "available": {
            "$ref": "#/components/schemas/AmountOrZero"
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "id",
          "object",
          "entity_id",
          "type",
          "name",
          "currency",
          "status",
          "balances",
          "primary_account_number_id",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "object": {
            "type": "string",
            "const": "account"
          },
          "entity_id": {
            "$ref": "#/components/schemas/EntityId"
          },
          "type": {
            "$ref": "#/components/schemas/AccountType"
          },
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "status": {
            "$ref": "#/components/schemas/AccountStatus"
          },
          "balances": {
            "$ref": "#/components/schemas/Balances"
          },
          "primary_account_number_id": {
            "description": "CVU shown as the origin to COELSA if the transfer does not specify another one.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AccountNumberId"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccountList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AccountNumberStatus": {
        "type": "string",
        "description": "`pending`: registration with COELSA in progress. `inactive`: deregistered or inactive at COELSA.",
        "enum": [
          "active",
          "inactive",
          "pending"
        ]
      },
      "Holder": {
        "type": "object",
        "required": [
          "name",
          "cuit"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "cuit": {
            "$ref": "#/components/schemas/Cuit"
          }
        }
      },
      "AccountNumber": {
        "type": "object",
        "required": [
          "id",
          "object",
          "account_id",
          "cvu",
          "alias",
          "status",
          "holder",
          "is_primary",
          "external_reference",
          "created_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AccountNumberId"
          },
          "object": {
            "type": "string",
            "const": "account_number"
          },
          "account_id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "cvu": {
            "$ref": "#/components/schemas/BankKey"
          },
          "alias": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Alias"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/AccountNumberStatus"
          },
          "holder": {
            "description": "Holder registered with COELSA. In phase 1 it is always the entity.",
            "allOf": [
              {
                "$ref": "#/components/schemas/Holder"
              }
            ]
          },
          "is_primary": {
            "type": "boolean"
          },
          "external_reference": {
            "description": "Label the client requested for this CVU (for example, the id of its user or merchant). No personal data.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExternalReference"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccountNumberList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccountNumber"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Destination": {
        "description": "Destination of a transfer or of a validation.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/DestinationCbu"
          },
          {
            "$ref": "#/components/schemas/DestinationCvu"
          },
          {
            "$ref": "#/components/schemas/DestinationAlias"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "cbu": "#/components/schemas/DestinationCbu",
            "cvu": "#/components/schemas/DestinationCvu",
            "alias": "#/components/schemas/DestinationAlias"
          }
        }
      },
      "DestinationCbu": {
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "const": "cbu"
          },
          "value": {
            "$ref": "#/components/schemas/BankKey"
          }
        }
      },
      "DestinationCvu": {
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "const": "cvu"
          },
          "value": {
            "$ref": "#/components/schemas/BankKey"
          }
        }
      },
      "DestinationAlias": {
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "const": "alias"
          },
          "value": {
            "$ref": "#/components/schemas/Alias"
          }
        }
      },
      "Counterparty": {
        "type": "object",
        "description": "The destination's holder and account according to COELSA, as verified at authorization.",
        "required": [
          "name",
          "cuit",
          "account",
          "account_type",
          "bank_code",
          "alias",
          "is_fcm_account"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "cuit": {
            "$ref": "#/components/schemas/Cuit"
          },
          "account": {
            "$ref": "#/components/schemas/BankKey"
          },
          "account_type": {
            "type": "string",
            "enum": [
              "cbu",
              "cvu"
            ]
          },
          "bank_code": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{3}$",
            "description": "Code of the financial institution of the CBU, if COELSA reports it."
          },
          "alias": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Alias"
              },
              {
                "type": "null"
              }
            ]
          },
          "is_fcm_account": {
            "type": "boolean",
            "description": "`true` if the destination is a Grip Rails account (the transfer is internal)."
          }
        }
      },
      "CounterpartyValidationRequest": {
        "type": "object",
        "required": [
          "destination"
        ],
        "additionalProperties": false,
        "properties": {
          "destination": {
            "$ref": "#/components/schemas/Destination"
          },
          "expected_cuit": {
            "description": "If present, the result is `holder_mismatch` when COELSA reports another CUIT.",
            "allOf": [
              {
                "$ref": "#/components/schemas/Cuit"
              }
            ]
          }
        }
      },
      "CounterpartyValidationResult": {
        "type": "string",
        "description": "`valid`: holder found, account active and controls in order. `not_found`: COELSA does not know the account or the\nalias. `inactive`: the account exists but is not active. `holder_mismatch`: the CUIT is not the expected one.\n`blocked`: Grip Rails cannot operate with that account (compliance control; no details).\n",
        "enum": [
          "valid",
          "not_found",
          "inactive",
          "holder_mismatch",
          "blocked"
        ]
      },
      "CounterpartyValidation": {
        "type": "object",
        "required": [
          "id",
          "object",
          "destination",
          "expected_cuit",
          "result",
          "display_message",
          "counterparty",
          "validated_at",
          "expires_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/CounterpartyValidationId"
          },
          "object": {
            "type": "string",
            "const": "counterparty_validation"
          },
          "destination": {
            "$ref": "#/components/schemas/Destination"
          },
          "expected_cuit": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Cuit"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "$ref": "#/components/schemas/CounterpartyValidationResult"
          },
          "display_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Text in Spanish to show the end user when the result is not `valid`."
          },
          "counterparty": {
            "description": "Present if COELSA reported a holder (`valid`, `inactive` and `holder_mismatch`).",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Counterparty"
              },
              {
                "type": "null"
              }
            ]
          },
          "validated_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Until when it can be used in a transfer (15 minutes)."
          }
        }
      },
      "OutgoingTransferCreateRequest": {
        "type": "object",
        "required": [
          "account_id",
          "amount",
          "currency",
          "destination"
        ],
        "additionalProperties": false,
        "properties": {
          "account_id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "account_number_id": {
            "description": "CVU of the account shown as the origin to COELSA. By default, the account's primary one.",
            "allOf": [
              {
                "$ref": "#/components/schemas/AccountNumberId"
              }
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "destination": {
            "$ref": "#/components/schemas/Destination"
          },
          "expected_counterparty_cuit": {
            "description": "Always recommended. If COELSA reports another CUIT for the holder, it is rejected with `destination_holder_mismatch`.",
            "allOf": [
              {
                "$ref": "#/components/schemas/Cuit"
              }
            ]
          },
          "counterparty_validation_id": {
            "description": "A current `valid` validation (15 minutes) of the same destination. If it expired or does not match, `400 parameter_invalid`.",
            "allOf": [
              {
                "$ref": "#/components/schemas/CounterpartyValidationId"
              }
            ]
          },
          "concept": {
            "$ref": "#/components/schemas/Concept"
          },
          "description": {
            "type": "string",
            "maxLength": 140,
            "description": "The client's text. It stays at Grip Rails; in phase 1 it does not travel to COELSA."
          },
          "external_reference": {
            "$ref": "#/components/schemas/ExternalReference"
          },
          "metadata": {
            "$ref": "#/components/schemas/Metadata"
          }
        }
      },
      "OutgoingTransferStatus": {
        "type": "string",
        "description": "See the table of states in the general description.",
        "enum": [
          "pending",
          "rejected",
          "reserved",
          "submitted",
          "completed",
          "failed"
        ]
      },
      "OutgoingTransferStatusReason": {
        "type": "string",
        "description": "Only in `submitted`. `awaiting_rail_result`: waiting for COELSA's notice (normal, seconds).\n`rail_outcome_unknown`: the result did not arrive within the expected time; Grip Rails is resolving it with COELSA and\nthe money stays on hold. Do not retry with another Idempotency-Key: you could pay twice.\n`late_settlement_review`: COELSA credited the transfer after we reported it to you as `failed` (late settlement). The\nmoney left and the amount was debited with a compensating entry; Grip Rails closes it as `completed` when two people\nconfirm the settlement.\n",
        "enum": [
          "awaiting_rail_result",
          "rail_outcome_unknown",
          "late_settlement_review"
        ]
      },
      "ChargeType": {
        "type": "string",
        "enum": [
          "fee",
          "tax"
        ]
      },
      "ChargeCode": {
        "type": "string",
        "description": "`fcm_fee`: Grip Rails fee. `idcb`: tax on bank debits and credits withheld by FCM Payment Solutions S.A.S. as withholding agent, when applicable.",
        "enum": [
          "fcm_fee",
          "idcb"
        ]
      },
      "Charge": {
        "type": "object",
        "required": [
          "type",
          "code",
          "amount",
          "description"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ChargeType"
          },
          "code": {
            "$ref": "#/components/schemas/ChargeCode"
          },
          "amount": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "description": {
            "type": "string",
            "maxLength": 140
          }
        }
      },
      "Iso20022OutgoingStatus": {
        "type": "object",
        "required": [
          "status",
          "reason"
        ],
        "properties": {
          "status": {
            "type": "string",
            "description": "pacs.002 `TransactionStatus` (ExternalPaymentTransactionStatus1Code).",
            "enum": [
              "PDNG",
              "ACCP",
              "ACSP",
              "ACCC",
              "RJCT"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Z0-9]{4}$",
            "description": "ExternalStatusReason1Code when `status` is `RJCT` (for example AM04, AC03, AC04, AB05, RR04)."
          }
        }
      },
      "AuthorizationCheckName": {
        "type": "string",
        "description": "`account`: entity and account enabled. `limits`: limits. `funds`: ledger hold. `counterparty`: the destination's holder\nat COELSA. `compliance`: sanctions lists and anti-fraud (reported together, without details).\n",
        "enum": [
          "account",
          "limits",
          "funds",
          "counterparty",
          "compliance"
        ]
      },
      "AuthorizationCheck": {
        "type": "object",
        "required": [
          "name",
          "result"
        ],
        "properties": {
          "name": {
            "$ref": "#/components/schemas/AuthorizationCheckName"
          },
          "result": {
            "type": "string",
            "enum": [
              "passed",
              "failed",
              "not_run"
            ]
          }
        }
      },
      "Rationale": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "`approved`, or the error code of the rejection (for example `insufficient_funds`).",
            "examples": [
              "approved"
            ]
          },
          "message": {
            "type": "string",
            "maxLength": 300
          }
        }
      },
      "Authorization": {
        "type": "object",
        "description": "Decision of the prior authorization, with its reason. FCM Payment Solutions S.A.S. also keeps the full detail of each control.",
        "required": [
          "decision",
          "decided_at",
          "rationale",
          "checks"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "description": "`approved`, `declined` or `pending` (the authorization has not finished yet; `decided_at` is the time it became pending).",
            "enum": [
              "approved",
              "declined",
              "pending"
            ]
          },
          "decided_at": {
            "type": "string",
            "format": "date-time"
          },
          "rationale": {
            "$ref": "#/components/schemas/Rationale"
          },
          "checks": {
            "type": "array",
            "description": "One result per control. `not_run` if it did not get to run because an earlier one failed.",
            "items": {
              "$ref": "#/components/schemas/AuthorizationCheck"
            }
          }
        }
      },
      "Reservation": {
        "type": "object",
        "description": "Hold in the ledger (pending transfers) for the amount plus charges.",
        "required": [
          "amount",
          "state",
          "placed_at",
          "resolved_at",
          "ledger_references"
        ],
        "properties": {
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "state": {
            "type": "string",
            "description": "`held`: on hold. `posted`: posted (the money left). `voided`: released. A transfer reopened by a late settlement stays\n`voided` (the hold was released when it failed and the amount was debited with a compensating entry); when Grip Rails\ncloses it as `completed`, free of charge, it becomes `posted` for the amount, with the references of the compensating\nentry.\n",
            "enum": [
              "held",
              "posted",
              "voided"
            ]
          },
          "placed_at": {
            "type": "string",
            "format": "date-time"
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ledger_references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerReference"
            }
          }
        }
      },
      "Rail": {
        "type": "object",
        "required": [
          "name",
          "operation_id",
          "origin_reference",
          "status_code",
          "submitted_at",
          "finalized_at"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "`coelsa`: transfer through COELSA. `fcm_book`: internal between Grip Rails accounts.",
            "enum": [
              "coelsa",
              "fcm_book"
            ]
          },
          "operation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the operation at COELSA.",
            "maxLength": 64
          },
          "origin_reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "`ori_trx_id` with which Grip Rails sent the operation. It does not change between retries.",
            "pattern": "^\\d{1,16}$"
          },
          "status_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Latest COELSA status code (for example 7541 persisted, 7500 credited).",
            "maxLength": 8
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finalized_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "FailureCode": {
        "type": "string",
        "description": "`rail_rejected`: COELSA rejected it (details in `rail.status_code` and `iso20022.reason`). `rail_expired`: it expired at\nCOELSA. `destination_closed`: the destination account does not exist or is closed at the time of crediting.\n`canceled_by_fcm`: Grip Rails canceled it before sending it (emergency stop, a control after the hold, or a `pending` transfer that could not be authorized).\n",
        "enum": [
          "rail_rejected",
          "rail_expired",
          "destination_closed",
          "canceled_by_fcm"
        ]
      },
      "Failure": {
        "type": "object",
        "required": [
          "code",
          "message",
          "display_message"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/FailureCode"
          },
          "message": {
            "type": "string",
            "maxLength": 300
          },
          "display_message": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          }
        }
      },
      "OutgoingTransfer": {
        "type": "object",
        "required": [
          "id",
          "object",
          "account_id",
          "account_number_id",
          "amount",
          "currency",
          "charges",
          "total_debit_amount",
          "destination",
          "counterparty",
          "concept",
          "description",
          "external_reference",
          "metadata",
          "status",
          "status_reason",
          "iso20022",
          "authorization",
          "reservation",
          "rail",
          "failure",
          "idempotency_key",
          "created_at",
          "updated_at",
          "completed_at",
          "failed_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/OutgoingTransferId"
          },
          "object": {
            "type": "string",
            "const": "outgoing_transfer"
          },
          "account_id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "account_number_id": {
            "description": "CVU shown as the origin to COELSA. `null` if it was rejected before one was chosen.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AccountNumberId"
              },
              {
                "type": "null"
              }
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "charges": {
            "type": "array",
            "description": "Charges for this transfer. Each one with an amount is a separate ledger entry. A transfer reopened by a late settlement and completed carries no charge and says so with an `fcm_fee` of `0` and its reason.",
            "items": {
              "$ref": "#/components/schemas/Charge"
            }
          },
          "total_debit_amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "destination": {
            "$ref": "#/components/schemas/Destination"
          },
          "counterparty": {
            "description": "Verified holder. `null` if it was rejected before the destination was looked up.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Counterparty"
              },
              {
                "type": "null"
              }
            ]
          },
          "concept": {
            "$ref": "#/components/schemas/Concept"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 140
          },
          "external_reference": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ExternalReference"
              },
              {
                "type": "null"
              }
            ]
          },
          "metadata": {
            "$ref": "#/components/schemas/Metadata"
          },
          "status": {
            "$ref": "#/components/schemas/OutgoingTransferStatus"
          },
          "status_reason": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/OutgoingTransferStatusReason"
              },
              {
                "type": "null"
              }
            ]
          },
          "iso20022": {
            "$ref": "#/components/schemas/Iso20022OutgoingStatus"
          },
          "authorization": {
            "$ref": "#/components/schemas/Authorization"
          },
          "reservation": {
            "description": "`null` if it was rejected before the hold.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reservation"
              },
              {
                "type": "null"
              }
            ]
          },
          "rail": {
            "description": "`null` until it is sent.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Rail"
              },
              {
                "type": "null"
              }
            ]
          },
          "failure": {
            "description": "Present only in `failed`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Failure"
              },
              {
                "type": "null"
              }
            ]
          },
          "idempotency_key": {
            "$ref": "#/components/schemas/IdempotencyKeyValue"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "failed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time at which it became `failed`. It goes back to `null` if it is reopened by a late settlement."
          }
        }
      },
      "OutgoingTransferList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutgoingTransfer"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Payer": {
        "type": "object",
        "description": "Who paid, according to COELSA's notice. Any field can be missing if COELSA does not report it.",
        "required": [
          "name",
          "cuit",
          "account",
          "account_type",
          "bank_code"
        ],
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "cuit": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Cuit"
              },
              {
                "type": "null"
              }
            ]
          },
          "account": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/BankKey"
              },
              {
                "type": "null"
              }
            ]
          },
          "account_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "cbu",
              "cvu",
              null
            ]
          },
          "bank_code": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{3}$"
          }
        }
      },
      "IncomingTransfer": {
        "type": "object",
        "required": [
          "id",
          "object",
          "account_id",
          "account_number_id",
          "cvu",
          "amount",
          "currency",
          "charges",
          "net_amount",
          "payer",
          "concept",
          "status",
          "iso20022",
          "rail",
          "ledger_references",
          "posted_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/IncomingTransferId"
          },
          "object": {
            "type": "string",
            "const": "incoming_transfer"
          },
          "account_id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "account_number_id": {
            "$ref": "#/components/schemas/AccountNumberId"
          },
          "cvu": {
            "$ref": "#/components/schemas/BankKey"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "charges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Charge"
            }
          },
          "net_amount": {
            "$ref": "#/components/schemas/AmountOrZero"
          },
          "payer": {
            "$ref": "#/components/schemas/Payer"
          },
          "concept": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Z]{3}$",
            "description": "BCRA purpose code reported by COELSA, if present."
          },
          "status": {
            "type": "string",
            "description": "In phase 1 always `posted`.",
            "enum": [
              "posted"
            ]
          },
          "iso20022": {
            "type": "object",
            "required": [
              "entry_status"
            ],
            "properties": {
              "entry_status": {
                "type": "string",
                "description": "camt.054 `EntryStatus`.",
                "enum": [
                  "BOOK"
                ]
              }
            }
          },
          "rail": {
            "type": "object",
            "required": [
              "name",
              "operation_id",
              "business_date"
            ],
            "properties": {
              "name": {
                "type": "string",
                "enum": [
                  "coelsa",
                  "fcm_book"
                ]
              },
              "operation_id": {
                "type": "string",
                "maxLength": 64,
                "description": "COELSA's id for the credit, or the originating `otr_…` if `name` is `fcm_book` (internal between Grip Rails accounts). It is the idempotency key of the ledger entry."
              },
              "business_date": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date",
                "description": "Business (clearing) date reported by COELSA."
              }
            }
          },
          "ledger_references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LedgerReference"
            }
          },
          "posted_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IncomingTransferList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IncomingTransfer"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "EventType": {
        "type": "string",
        "description": "Phase 1 types. New types can appear without a version notice; ignore the ones you do not know.\n`transfer.outgoing.reopened`: a `failed` transfer that COELSA credited later went back to `submitted`\n(`status_reason: late_settlement_review`).\n",
        "enum": [
          "transfer.incoming.posted",
          "transfer.outgoing.reserved",
          "transfer.outgoing.completed",
          "transfer.outgoing.failed",
          "transfer.outgoing.reopened"
        ]
      },
      "EventData": {
        "type": "object",
        "description": "Thin reference to the object. For the current state, fetch the object.",
        "required": [
          "object",
          "id",
          "account_id",
          "ledger_references"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "incoming_transfer",
              "outgoing_transfer"
            ]
          },
          "id": {
            "type": "string",
            "maxLength": 40
          },
          "account_id": {
            "$ref": "#/components/schemas/AccountId"
          },
          "ledger_references": {
            "type": "array",
            "description": "Ledger entries that produced the event.",
            "items": {
              "$ref": "#/components/schemas/LedgerReference"
            }
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "object",
          "type",
          "sequence",
          "entity_id",
          "api_version",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/EventId"
          },
          "object": {
            "type": "string",
            "const": "event"
          },
          "type": {
            "$ref": "#/components/schemas/EventType"
          },
          "sequence": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Contiguous and increasing per entity, assigned in the order in which the change was confirmed."
          },
          "entity_id": {
            "$ref": "#/components/schemas/EntityId"
          },
          "api_version": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "$ref": "#/components/schemas/EventData"
          }
        }
      },
      "EventList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_after_sequence"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_after_sequence": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Value for the next `after_sequence` (the `sequence` of the last event returned)."
          }
        }
      },
      "IdempotencyRecord": {
        "type": "object",
        "required": [
          "key",
          "object",
          "status",
          "method",
          "path",
          "request_fingerprint",
          "response_status",
          "resource",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "key": {
            "$ref": "#/components/schemas/IdempotencyKeyValue"
          },
          "object": {
            "type": "string",
            "const": "idempotency_key"
          },
          "status": {
            "type": "string",
            "enum": [
              "in_progress",
              "completed"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "POST"
            ]
          },
          "path": {
            "type": "string",
            "maxLength": 200
          },
          "request_fingerprint": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 of method, path and canonicalized body (RFC 8785)."
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 200,
            "maximum": 499
          },
          "resource": {
            "description": "Object created or affected, if any.",
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "object",
                  "id"
                ],
                "properties": {
                  "object": {
                    "type": "string"
                  },
                  "id": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ErrorType": {
        "type": "string",
        "enum": [
          "invalid_request_error",
          "authentication_error",
          "permission_error",
          "not_found_error",
          "idempotency_error",
          "transfer_declined",
          "rate_limit_error",
          "service_error"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "description": "See the table of codes in the general description.",
        "enum": [
          "invalid_request",
          "parameter_missing",
          "parameter_invalid",
          "amount_invalid",
          "currency_not_supported",
          "concept_not_supported",
          "destination_invalid",
          "unsupported_version",
          "idempotency_key_missing",
          "idempotency_key_invalid",
          "api_key_missing",
          "api_key_invalid",
          "signature_missing",
          "signature_invalid",
          "signature_expired",
          "signature_replayed",
          "signature_key_unknown",
          "content_digest_mismatch",
          "ip_not_allowed",
          "environment_mismatch",
          "entity_suspended",
          "not_found",
          "idempotency_key_reused",
          "idempotency_request_in_progress",
          "insufficient_funds",
          "account_frozen",
          "account_closed",
          "limit_per_transfer_exceeded",
          "limit_daily_exceeded",
          "limit_monthly_exceeded",
          "destination_not_found",
          "destination_inactive",
          "destination_is_source",
          "destination_holder_mismatch",
          "compliance_rejected",
          "risk_rejected",
          "rate_limited",
          "internal_error",
          "rail_unavailable",
          "ledger_unavailable",
          "transfers_paused",
          "service_unavailable"
        ]
      },
      "ErrorBody": {
        "type": "object",
        "required": [
          "type",
          "code",
          "message",
          "display_message",
          "param",
          "request_id",
          "retryable",
          "doc_url",
          "transfer_id"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ErrorType"
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": "string",
            "description": "Technical explanation for the integrator. Do not show it to end users."
          },
          "display_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Text in Spanish suitable for showing to an end user, or `null`."
          },
          "param": {
            "type": [
              "string",
              "null"
            ],
            "description": "Field or header that caused the error, if applicable."
          },
          "request_id": {
            "$ref": "#/components/schemas/RequestId"
          },
          "retryable": {
            "type": "boolean",
            "description": "`true`: retry the same request with the same Idempotency-Key."
          },
          "doc_url": {
            "type": "string",
            "format": "uri"
          },
          "transfer_id": {
            "description": "In a transfer `422`, the id of the object that was left in `rejected`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/OutgoingTransferId"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorBody"
          }
        }
      },
      "JwkOkp": {
        "type": "object",
        "required": [
          "kty",
          "crv",
          "x",
          "kid",
          "alg",
          "use"
        ],
        "properties": {
          "kty": {
            "type": "string",
            "const": "OKP"
          },
          "crv": {
            "type": "string",
            "const": "Ed25519"
          },
          "x": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "kid": {
            "type": "string",
            "maxLength": 64
          },
          "alg": {
            "type": "string",
            "const": "EdDSA"
          },
          "use": {
            "type": "string",
            "const": "sig"
          }
        }
      },
      "JwkEc": {
        "type": "object",
        "required": [
          "kty",
          "crv",
          "x",
          "y",
          "kid",
          "alg",
          "use"
        ],
        "properties": {
          "kty": {
            "type": "string",
            "const": "EC"
          },
          "crv": {
            "type": "string",
            "const": "P-256"
          },
          "x": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "y": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "kid": {
            "type": "string",
            "maxLength": 64
          },
          "alg": {
            "type": "string",
            "const": "ES256"
          },
          "use": {
            "type": "string",
            "const": "sig"
          }
        }
      },
      "Jwks": {
        "type": "object",
        "required": [
          "keys"
        ],
        "properties": {
          "keys": {
            "type": "array",
            "minItems": 1,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/JwkOkp"
                },
                {
                  "$ref": "#/components/schemas/JwkEc"
                }
              ]
            }
          }
        }
      },
      "SimulateIncomingTransferRequest": {
        "type": "object",
        "required": [
          "account_number_id",
          "amount"
        ],
        "additionalProperties": false,
        "properties": {
          "account_number_id": {
            "$ref": "#/components/schemas/AccountNumberId"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "payer": {
            "type": "object",
            "additionalProperties": false,
            "description": "Data of the simulated payer. If missing, a synthetic one is used.",
            "properties": {
              "name": {
                "type": "string",
                "maxLength": 120
              },
              "cuit": {
                "$ref": "#/components/schemas/Cuit"
              },
              "account": {
                "$ref": "#/components/schemas/BankKey"
              }
            }
          },
          "concept": {
            "$ref": "#/components/schemas/Concept"
          }
        }
      },
      "SimulateRailResultRequest": {
        "type": "object",
        "required": [
          "outcome"
        ],
        "additionalProperties": false,
        "properties": {
          "outcome": {
            "type": "string",
            "enum": [
              "completed",
              "failed"
            ]
          },
          "coelsa_code": {
            "type": "string",
            "pattern": "^\\d{2,4}$",
            "description": "COELSA code to simulate. By default 7500 if `completed` and 7699 if `failed`."
          }
        }
      },
      "SimulateLateSettlementReviewRequest": {
        "type": "object",
        "required": [
          "outcome"
        ],
        "additionalProperties": false,
        "properties": {
          "outcome": {
            "type": "string",
            "enum": [
              "completed"
            ],
            "description": "The only outcome of the review of a late settlement: two people at Grip Rails confirm that the money left, and the transfer is closed as `completed`, free of charge."
          }
        }
      },
      "SandboxClock": {
        "type": "object",
        "required": [
          "object",
          "entity_id",
          "now",
          "offset_seconds"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "sandbox_clock"
          },
          "entity_id": {
            "$ref": "#/components/schemas/EntityId"
          },
          "now": {
            "type": "string",
            "format": "date-time",
            "description": "The entity's time in the sandbox."
          },
          "offset_seconds": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "How far ahead it is of the real time."
          }
        }
      },
      "AdvanceClockRequest": {
        "type": "object",
        "required": [
          "advance_seconds"
        ],
        "additionalProperties": false,
        "properties": {
          "advance_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2678400,
            "description": "Seconds to advance (up to 31 days per request)."
          }
        }
      }
    }
  }
}
