# Inyo Global Developer Documentation — Full Content > Complete documentation optimized for LLM consumption. > Inyo Global is a licensed money movement infrastructure provider (50-state MTL holder). > Base URLs: Sandbox: https://sandbox-gw.simpleps.com | Production: https://gw.simpleps.com > Auth: OAuth 2.0 Bearer token via POST /oauth/token with clientId + secretId. ## Table of Contents - Welcome to the Inyo API Docs → path: index - Our products → path: api-references/readme/our-products - Payments Gateway → path: api-references/payments-gateway - Getting started → path: api-references/payments-gateway/getting-started - Background → path: api-references/payments-gateway/background - System architecture → path: api-references/payments-gateway/background/system-architecture - Gateway environment settings → path: api-references/payments-gateway/background/gateway-environment-settings - Authentication Methods → path: api-references/payments-gateway/authentication-methods - Environments → path: api-references/payments-gateway/environments - Technical Resources → path: api-references/payments-gateway/technical-resources - Mitigating Fraud → path: api-references/payments-gateway/mitigating-fraud - APIs → path: api-references/payments-gateway/apis - Tokenizing cards → path: api-references/payments-gateway/apis/tokenizing-cards - Check Card Account (ANI) → path: api-references/payments-gateway/apis/check-card-account - Payment → path: api-references/payments-gateway/apis/payment - Pulling funds → path: api-references/payments-gateway/apis/payment/pulling-funds - Cards → path: api-references/payments-gateway/apis/payment/pulling-funds/cards - Authorizing → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/authorizing - Handling AVS / CVC → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/authorizing/handling-avs-cvc - Handling 3D secure → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/authorizing/handling-3d-secure - Capture → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/capture - Refund → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/refund - Void → path: api-references/payments-gateway/apis/payment/pulling-funds/cards/void - ACH (Bank account) → path: api-references/payments-gateway/apis/payment/pulling-funds/ach-bank-account - Listing payments → path: api-references/payments-gateway/apis/payment/pulling-funds/listing-payments - By external id → path: api-references/payments-gateway/apis/payment/pulling-funds/listing-payments/by-external-id - Get a list of payments → path: api-references/payments-gateway/apis/payment/pulling-funds/listing-payments/get-a-list-of-payments - Push Transaction → path: api-references/payments-gateway/apis/payment/push-transaction - Pull and push in one step → path: api-references/payments-gateway/apis/payment/pull-and-push-in-one-step - Webhooks → path: api-references/payments-gateway/apis/webhooks - Domain tables → path: api-references/payments-gateway/apis/domain-tables - Response Code → path: api-references/payments-gateway/apis/domain-tables/response-code - Payment Status → path: api-references/payments-gateway/apis/domain-tables/payment-status - Test data → path: api-references/payments-gateway/apis/test-data - Cards → path: api-references/payments-gateway/apis/test-data/cards - Payment Link → path: api-references/payments-gateway/payment-link - Smart Terminal → path: api-references/payments-gateway/smart-terminal - Remittances → path: api-references/remittances - Getting Started → path: api-references/remittances/getting-started - Authentication → path: api-references/remittances/authentication - Data Population → path: api-references/remittances/data-population - Payout countries enabled → path: api-references/remittances/data-population/payout-countries-enabled - Recipient schema → path: api-references/remittances/data-population/recipient-schema - Recipient account schema → path: api-references/remittances/data-population/recipient-account-schema - Transaction schema → path: api-references/remittances/data-population/transaction-schema - Banks in a country → path: api-references/remittances/data-population/banks-in-a-country - Quotes (fx) → path: api-references/remittances/quotes-fx - Sender → path: api-references/remittances/sender - Trust level limits → path: api-references/remittances/sender/trust-level-limits - Uploading documents → path: api-references/remittances/sender/uploading-documents - Test data → path: api-references/remittances/sender/test-data - Sender funding account → path: api-references/remittances/sender-funding-account - Recipient → path: api-references/remittances/recipient - Recipient account → path: api-references/remittances/recipient-account - Transaction → path: api-references/remittances/transaction - Webhooks → path: api-references/remittances/webhooks - Assets → path: assets --- description: >- Inyo Global Developer Portal — APIs for payment gateway (card/ACH), cross-border remittances (165+ countries), card tokenization, 3DS, KYC/AML automation, and real-time webhooks. --- # 👋 Welcome to the Inyo API Docs Welcome to the Inyo Global Developer Portal — your gateway to building powerful, secure, and scalable payment solutions. Our suite of APIs empowers developers to integrate a wide range of financial services into their applications, from cross-border payments and currency exchange to card tokenization, 3DS authentication, and real-time transaction tracking. Whether you’re a fintech startup, e-commerce platform, or enterprise solution, our APIs are designed to be developer-friendly, compliant, and built for performance. ### 🌟 What You Can Do with Inyo Global APIs * Receive and manage payments (AFT , OCT, P2P, ACH, card) with dynamic routing and fraud control * Send domestic & international payouts through our proprietary network spanning 150+ countries * Automate KYC, AML, and compliance workflows, including user onboarding, identity verification, and real-time monitoring * Tokenize and manage cards securely, with PCI-grade protection * Support multi-currency conversions and settlement, helping you operate globally with confidence * Trigger and handle 3D Secure (3DS) authentication for added transactional safety * Track, reconcile, and report transactions in real time * Hook into our webhooks system for instant event-driven updates on payment and compliance events ### 🧰 Tools & Libraries * RESTful API endpoints * JSON-based payloads * SDKs and code samples (coming soon) * Pre-configured support for major 3DS providers and acquiring networks ### 📘 Need Help? * Visit our Authentication Guide to learn how to securely access our APIs * Check out Common Use Cases for step-by-step implementation examples * Reach out to our sales team for assistance ## Quick Reference | Resource | URL | |---|---| | **Sandbox API** | `https://sandbox-gw.simpleps.com` | | **Production API** | `https://gw.simpleps.com` | | **Auth endpoint** | `POST /oauth/token` | | **Card tokenizer (Sandbox)** | `https://cdn.simpleps.com/sandbox/inyo.js` | | **Card tokenizer (Production)** | `https://cdn.simpleps.com/production/inyo.js` | | **LLM-optimized docs** | `/llms.txt` / `/llms-full.txt` | | **AI coding assistant guide** | `/skills/inyo-best-practices` | --- description: >- Overview of Inyo Global's product suite: Payment Gateway, Payment Link, Smart Terminal, and Remittances API — a complete omnichannel platform for card, ACH, remote, and in-store payments plus cross-border transfers. --- # Our products We offer a complete suite of payment solutions tailored to different operational needs: Payments Gateway, Payment Link, Smart Terminal, and Remittances API. Whether you need online payment processing, remote collection, in-store terminals, or a turnkey cross-border remittance stack, we've got you covered. *** ### 🔗 Payments Gateway Want to send or receive payments and leverage your own infrastructure? Payments Gateway is for you. Streamlined domestic and international payments for platforms of all sizes Our Payments Gateway API is designed for businesses that want to send or receive payments efficiently — domestically or internationally — while retaining control over the user experience and compliance policies. #### ✅ Ideal for: * Domestic platforms looking to streamline fund collection and payouts * Marketplaces, SaaS platforms, and service providers managing supplier or user disbursements * Licensed fintechs or financial institutions with their own compliance stack #### 🚀 Key Features: * Handle domestic and cross-border payments using card (AFT/OCT), bank transfers, and ACH * Leverage our orchestration platform, maximizing anti-fraud controls and different routes for authorization * Tokenize and store cards securely with PCI-compliant infrastructure * Trigger 3D Secure (3DS) authentication for enhanced payment security * Integrate webhooks for payment status updates and reconciliation tools * Maintain control over KYC, fraud, and compliance processes, if desired *** ### 📱 Payment Link Collect payments remotely — customers receive a payment link on their phone and fund transactions in real time Payment Link is a remote payment solution designed for AFT programs. Customers receive a secure payment link via SMS or WhatsApp to fund money transfers, top-ups, bill payments, prepaid card loads, or wallet balances. The link is generated via API and the result is sent back to your system in real time via webhook. #### ✅ Ideal for: * Money transfer operators accepting remote funding from senders * Top-up and bill pay platforms collecting payments without a physical terminal * Prepaid card programs enabling remote card loading * Any AFT program that needs to collect card payments without the customer being present #### 🚀 Key Features: * Generate payment links programmatically via API * Deliver links via SMS or WhatsApp * Real-time payment processing and instant webhook notifications * PCI-compliant checkout with 3D Secure support * No app required — customers pay from their phone browser *** ### 🖥️ Smart Terminal Accept in-person card payments with NFC tap-to-pay and manual card entry at agent locations The Smart Terminal is a card-present payment solution designed for AFT programs. It supports NFC contactless payments from cards, phones, and wearables, as well as manual card entry. It runs on the same platform as the Payments Gateway and Payment Link, sharing routing intelligence and fraud prevention. #### ✅ Ideal for: * Money transfer agent locations processing walk-in customers * Top-up and bill pay points of service * Prepaid card loading at physical locations * Any AFT program that processes in-person card payments #### 🚀 Key Features: * NFC tap-to-pay for contactless card, phone, and wearable payments * Manual card entry for fallback scenarios * Lower card-present interchange rates * Unified reporting across all channels (API, remote, in-person) * Smart routing through the Inyo gateway for optimal authorization rates *** ### 🔗 Better Together: Gateway + Payment Link + Smart Terminal The Payments Gateway, Payment Link, and Smart Terminal are designed to work as a unified AFT platform. Running all three products together increases authorization rates and revenue by sharing routing intelligence, fraud signals, and transaction data across every payment channel. | Channel | Solution | | :--- | :--- | | API / Online | Payments Gateway API | | Remote / Mobile | Payment Link | | In-person / Card present | Smart Terminal | *** ### 🌍 Remittances API Cross-border payouts with built-in compliance, KYC, and licensing The Remittances API builds on our Gateway infrastructure and adds a full regulatory layer — including KYC onboarding, AML screening, and audit-ready transaction controls. It’s the ideal solution for platforms looking to offer international transfers or gig payouts without managing complex compliance requirements. #### ✅ Ideal for: * Platforms without payment licenses * Gig and creator economy tools * Global payroll and disbursement platforms * Remittance startups or apps looking to launch quickly under a compliant framework #### 🧩 What’s Included: * All features of the Payments Gateway, plus: * Built-in KYC/KYB onboarding flows * Real-time AML & sanctions screening * Transaction monitoring and fraud detection * Regulatory reporting and compliance management * Access to Inyo Global’s licensing coverage *** ### 🔍 Choosing the Right Product | Feature | Payments Gateway | Payment Link | Smart Terminal | Remittances API | | :--- | :--- | :--- | :--- | :--- | | Channel | API / Online | Remote / Mobile | In-person / Card present | Cross-border transfers | | Integration | API | API | Terminal device | API | | AFT Programs | ✅ Money transfer, top-up, bill pay, prepaid, wallets | ✅ Money transfer, top-up, bill pay, prepaid, wallets | ✅ Money transfer, top-up, bill pay, prepaid, wallets | ✅ Cross-border money transfer | | Card-Not-Present | ✅ Yes | ✅ Yes | — | ✅ Yes | | Card-Present (NFC) | — | — | ✅ Yes | — | | Delivery Method | — | SMS / WhatsApp | — | — | | Webhook Notifications | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | | International Payouts | ✅ Yes (with license) | — | — | ✅ Yes (license included) | | KYC/AML/Compliance | Bring Your Own | Bring Your Own | Bring Your Own | Fully built-in | | Best For | API-driven AFT collection and payouts | Remote AFT collection via SMS/WhatsApp | In-person AFT collection at agent locations | Turnkey cross-border transfers | *** #### 💡 Let’s Get Started Whether you need modern rails for domestic payments or a fully managed remittance stack, Inyo Global makes it easy to build, scale, and stay compliant. 👉 [Explore the docs](https://dev.inyoglobal.com) or [contact us](mailto:support@inyoglobal.com) to learn more. --- description: >- Welcome to the Inyo Payment Gateway documentation. Accept card and ACH payments, send domestic and international payouts, and manage the full transaction lifecycle. --- # Payments Gateway The Inyo Payment Gateway provides a unified API for **pull** (collect) and **push** (payout) operations across multiple payment methods and providers. ## Capabilities | Feature | Description | |---|---| | **Card Payments** | Accept Visa, Mastercard, Amex, and Discover via tokenized card data | | **ACH Transfers** | Pull funds from US bank accounts | | **International Payouts** | Push to cards, bank accounts, PIX, and wallets in 165+ countries | | **3D Secure** | Built-in 3DS authentication with Cardinal | | **Smart Routing** | Automatic provider selection for optimal success rates | | **Fraud Prevention** | AVS, CVC verification, velocity controls, and built-in anti-fraud | | **Payment Link** | Remote AFT collection via SMS or WhatsApp — money transfer, top-up, bill pay, prepaid, and wallets | | **Smart Terminal** | In-person AFT collection with NFC tap-to-pay and manual card entry at agent locations | ## Documentation Guide | Section | What You'll Learn | |---|---| | [**Getting Started**](getting-started.md) | Quick start guide with end-to-end payment example | | [**Authentication**](authentication-methods.md) | OAuth 2.0 token flow | | [**Environments**](environments.md) | Sandbox and production URLs | | [**Technical Resources**](technical-resources.md) | API keys, rate limits, OpenAPI spec | | [**APIs**](apis/) | All endpoints: tokenization, payments, capture, void, refund | | [**Webhooks**](apis/webhooks.md) | Real-time transaction notifications | | [**Test Data**](apis/test-data/cards.md) | Sandbox cards and simulation codes | | [**Mitigating Fraud**](mitigating-fraud.md) | Security best practices | | [**Payment Link**](payment-link.md) | Remote AFT collection via SMS/WhatsApp payment links | | [**Smart Terminal**](smart-terminal.md) | In-person AFT collection with NFC and manual card entry | | [**Background**](background/) | Architecture and infrastructure overview | --- description: >- Everything you need to accept payments, send payouts, and manage the full transaction lifecycle with the Inyo Payment Gateway. --- # Getting Started ## Overview The Inyo Payment Gateway enables you to: - **Pull funds** — Charge customers via credit/debit cards or ACH bank transfers - **Push funds** — Send payouts domestically (ACH) or internationally (cards, PIX, wallets, bank accounts) - **Pull + Push** — Collect and disburse in a single API call All operations follow a consistent REST API pattern with OAuth 2.0 authentication. ## Prerequisites Before you begin integrating, you'll need three credentials from the Inyo team: | Credential | Format | Purpose | |---|---|---| | **Public Key** | `a23271e1-c1c0-44d3-...` | Card tokenization via the JavaScript library | | **Client ID** | `MY_CLIENT_ID` | OAuth authentication (`/oauth/token`) | | **Client Secret** | `4efa3460-a121-1ca9-...` | OAuth authentication (`/oauth/token`) | > **To obtain credentials**, contact the Inyo commercial team. During onboarding, you'll also provide your origin URL(s) for CORS whitelisting. ## Integration Flow Here's the typical integration path for a card payment: ``` 1. Authenticate → POST /oauth/token 2. Tokenize card → Client-side via inyo.js 3. Create payment → POST /v2/payment 4. Handle 3DS → Redirect to redirectAcsUrl (if CHALLENGE) 5. Capture payment → POST /payments/{id}/capture 6. (Optional) Void → POST /payments/{id}/void 7. (Optional) Refund → POST /payments/{id}/refund ``` For ACH and push payments, skip step 2 (tokenization) and provide bank/destination details directly in the payment payload. ## Quick Example: Authorize a Card Payment ### Step 1 — Get an access token ```bash curl -X POST https://sandbox-gw.simpleps.com/oauth/token \ -H 'Content-Type: application/json' \ -d '{ "clientId": "YOUR_CLIENT_ID", "secretId": "YOUR_CLIENT_SECRET" }' ``` **Response:** ```json { "accessToken": "eyJhbGciOiJSUzI1NiIs...", "tokenType": "Bearer", "expiresIn": 3600 } ``` ### Step 2 — Tokenize a card (client-side) Load the tokenizer library and create a token from the cardholder's data. See [Tokenizing Cards](apis/tokenizing-cards.md) for the full guide. ```html ``` ```javascript const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', storeLaterUse: false, successCallback: (response) => { // response.additionalData.token contains the card token submitPayment(response.additionalData.token); }, errorCallback: (error) => { console.error('Tokenization failed:', error.code); } }); // Call when user clicks "Pay" tokenizer.tokenizeCard(); ``` ### Step 3 — Create the payment ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "order-12345", "ipAddress": "203.0.113.42", "paymentType": "PULL", "capture": false, "amount": { "total": 99.99, "currency": "USD" }, "sender": { "firstName": "John", "lastName": "Smith", "address": { "countryCode": "US", "stateCode": "NY", "city": "New York", "line1": "123 Main Street", "zipCode": "10001" }, "paymentMethod": { "type": "CARD", "cardTokenId": "ab5fc589-8b48-4531-94c0-68b0629c13fe" } } }' ``` **Response (3DS Challenge required):** ```json { "status": 200, "data": { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": 99.99, "status": "CHALLENGE", "redirectAcsUrl": "https://sandbox-gw.simpleps.com/secure-code/start-challenge?token=dce568c6-...", "approved": false, "captured": false, "message": "Payment awaiting 3DS challenge verification" } } ``` **Response (Authorized directly):** ```json { "status": 200, "data": { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": 99.99, "status": "AUTHORIZED", "approved": true, "captured": false, "message": "Payment Approved", "responseCode": "00", "cvcResult": "APPROVED", "avsResult": "APPROVED" } } ``` ### Step 4 — Capture when ready ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/capture \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'Content-Type: application/json' ``` ## Payment Lifecycle ``` ┌──────────┐ │ CHALLENGE│ ──(3DS fail)──→ DECLINED └────┬─────┘ │ (3DS success) ▼ ┌─────────┐ ┌────────────┐ ┌─────────┐ │ Payment │────→│ AUTHORIZED │────→│ CAPTURED │────→ REFUNDED │ Created │ └──────┬─────┘ └─────────┘ (full/partial) └─────────┘ │ ▼ VOIDED ``` - **CHALLENGE** → Cardholder must complete 3DS verification - **AUTHORIZED** → Funds reserved; you can capture or void - **CAPTURED** → Funds settled; you can refund (full or partial) - **VOIDED** → Authorization cancelled before capture - **DECLINED** → Payment rejected by issuer or fraud rules ## Environments | Environment | API Base URL | Tokenizer URL | |---|---|---| | **Sandbox** | `https://sandbox-gw.simpleps.com` | `https://cdn.simpleps.com/sandbox/inyo.js` | | **Production** | `https://gw.simpleps.com` | `https://cdn.simpleps.com/production/inyo.js` | ## What's Next | Topic | Description | |---|---| | [Authentication](authentication-methods.md) | OAuth 2.0 token details and best practices | | [Tokenizing Cards](apis/tokenizing-cards.md) | Client-side card tokenization with `inyo.js` | | [Pulling Funds](apis/payment/pulling-funds/) | Card and ACH payment collection | | [Push Transactions](apis/payment/push-transaction.md) | Domestic and international payouts | | [3D Secure](apis/payment/pulling-funds/cards/authorizing/handling-3d-secure.md) | Challenge flow and redirect handling | | [Webhooks](apis/webhooks.md) | Real-time transaction status notifications | | [Test Data](apis/test-data/cards.md) | Sandbox test cards and simulation codes | | [Technical Resources](technical-resources.md) | API keys, URLs, rate limits, and OpenAPI spec | --- description: >- Architecture background for the Inyo Payment Gateway — system design, component overview, and environment configuration reference. --- # Background --- description: >- High-level system architecture of the Inyo Payment Gateway: component diagram, data flow, acquiring network topology, and security boundaries. --- # System architecture #### 1. **Microservices Architecture** Inyo gateway is built on a microservices architecture, where each functionality is encapsulated in a standalone service, compiled and tested and deployed automatically. This approach ensures: * Modular development and deployment. * High scalability to handle varying workloads. * Fault isolation, where issues in one service do not affect others. Each microservice is containerized, enabling consistent environments and seamless deployment across different platforms #### 2. **Payment Router Mechanism** A central component of the architecture is the **Payment Router**, which dynamically selects the optimal payment route based on: * **Transaction Type**: Determines whether the operation is a **pull** or **push**. * **Payment Method**: Identifies the payment type (e.g., **ACH**, **Card**, or **different payouts formats**). * **Currency**: Considers the currencies of the origin and destination countries to ensure compatibility and compliance with local regulations. The router ensures that every transaction is processed efficiently and routed to the appropriate provider, optimizing performance and reliability. #### 3. **Provider-Specific Microservices** To cater to the diverse requirements of payment providers, each provider has its own dedicated microservice. These microservices: * Implement the specific rules and standards mandated by their respective providers. * Manage provider-specific APIs, ensuring seamless integration. * Are independently deployable, allowing for quick updates and scaling. #### 4. **Integration with Payment Router** The provider microservices are tightly integrated with the **Payment Router**, making them readily available for the gateway to: * Offer a wide range of payment integrations to client banks. * Dynamically scale as new providers are added, enhancing the system’s flexibility. This tightly coupled design ensures that the gateway remains extensible and capable of supporting future integrations without disrupting existing services. #### 5. **Scalability and Security** The architecture leverages modern design principles to ensure: * **Horizontal Scalability**: Services can scale independently based on load. * **Enhanced Security**: Each microservice operates in an isolated environment, reducing vulnerabilities. This architecture makes Inyo gateway a powerful tool for businesses, enabling seamless financial transactions while maintaining the highest standards of performance and reliability. --- description: >- To ensure secure, reliable, and efficient operations, Inyo gateway leverages the following technologies and services --- # Gateway environment settings #### 1. **Authentication and Security with IdP** Inyo gateway uses **IdP** for authentication and security management. The IdP enables: * Centralized user management. * Support for various authentication protocols (e.g., OAuth2, OpenID Connect). #### 2. **Publish/Subscriber Architecture** Inyo gateway processes transactions using a **publish/subscribe architecture** powered by **Industry Standards**. This mechanism ensures: * Decoupling between services, allowing for independent scaling. * Efficient transaction processing by distributing tasks among multiple consumers. * High reliability through message durability and retry mechanisms. #### 3. **KMS for Data Encryption** To protect sensitive data, Inyo gateway uses **KMS (Key Management Service)** to create and manage encryption keys. These keys are used to securely store: * Card details. * Provider credentials. * Other sensitive information that requires strict security compliance. By integrating these technologies, Inyo gateway achieves a secure and scalable environment for managing financial transactions across diverse providers and payment methods. --- description: >- Inyo Gateway uses OAuth 2.0 Client Credentials to authenticate all API requests. This page explains how to obtain and use access tokens. --- # Authentication ## Overview All Payment Gateway API calls (except tokenization, which uses your public key) require a Bearer token obtained via the `/oauth/token` endpoint. **Key characteristics:** - **Grant type**: Client Credentials (machine-to-machine) - **Token lifetime**: Short-lived (typically 1 hour) — request a new token when expired - **Transport**: Always use HTTPS; never send credentials over unencrypted connections - **Storage**: Tokens should be stored securely server-side; never expose them in client-side code ## Obtaining an Access Token ``` POST /oauth/token ``` ### Request ```bash curl -X POST https://sandbox-gw.simpleps.com/oauth/token \ -H 'Content-Type: application/json' \ -d '{ "clientId": "YOUR_CLIENT_ID", "secretId": "YOUR_CLIENT_SECRET" }' ``` | Field | Type | Required | Description | |---|---|---|---| | `clientId` | string | ✅ | Your OAuth client identifier, provided during onboarding | | `secretId` | string | ✅ | Your OAuth client secret, provided during onboarding | ### Response (Success — 200) ```json { "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "tokenType": "Bearer", "expiresIn": 3600 } ``` | Field | Type | Description | |---|---|---| | `accessToken` | string | JWT token to include in subsequent API calls | | `tokenType` | string | Always `"Bearer"` | | `expiresIn` | number | Token validity in seconds | ### Response (Unauthorized — 401) ```json { "status": 401, "message": "Unauthorized request", "responseCode": "SE_001" } ``` ## Using the Token Include the access token in the `Authorization` header of every API request: ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ ... }' ``` ## Token Management Best Practices 1. **Cache tokens** — Reuse the same token until it expires rather than requesting a new one per API call. The authentication endpoint has a rate limit of 60 requests/minute. 2. **Handle expiry gracefully** — When you receive a `401` or `403` response, request a new token and retry the original request. 3. **Never expose credentials client-side** — The `clientId` and `secretId` must only be used from your backend server (Backend-for-Frontend pattern). The only client-side credential is the `publicKey` used for card tokenization. 4. **Rotate secrets periodically** — Contact the Inyo team to rotate your client secret if you suspect it has been compromised. ## Authentication vs. Tokenization | Concern | Server-side (OAuth) | Client-side (Tokenizer) | |---|---|---| | **Credential** | `clientId` + `secretId` | `publicKey` | | **Endpoint** | `POST /oauth/token` | `inyo.js` library | | **Purpose** | Authenticate API calls | Tokenize card data | | **Exposure** | Backend only | Browser (safe to expose) | ## Rate Limits | Endpoint | Limit | |---|---| | Authentication (`/oauth/token`) | 60 requests/minute | | FX rates | 200 requests/minute | | All other authenticated endpoints | 400 requests/minute | When rate-limited, the API returns HTTP `429`. Implement exponential backoff with jitter in your retry logic. --- description: >- Inyo provides separate sandbox and production environments. Use sandbox for development and testing with simulated transactions. --- # Environments ## URLs | Resource | Sandbox | Production | |---|---|---| | **API Base** | `https://sandbox-gw.simpleps.com` | `https://gw.simpleps.com` | | **Tokenizer JS** | `https://cdn.simpleps.com/sandbox/inyo.js` | `https://cdn.simpleps.com/production/inyo.js` | | **OpenAPI Spec** | [sandbox-gw.simpleps.com/q/swagger-ui](https://sandbox-gw.simpleps.com/q/swagger-ui/#/) | — | ## Sandbox Environment Use the sandbox for development and testing. Transactions are simulated — no real money moves and no card networks are contacted. **Sandbox features:** - Full API functionality matching production behavior - [Test cards](apis/test-data/cards.md) for simulating approvals, declines, 3DS challenges, and AVS results - Simulation codes via cardholder name (e.g., `REFUSED` triggers a decline) - Webhook delivery to your configured endpoints **Sandbox limitations:** - No real card network authorization - Some issuer-specific behaviors may differ from production - Rate limits may be lower than production ## Production Environment Use production for live, real-money transactions. Ensure all integrations are thoroughly tested in sandbox before going live. **Before going to production:** - [ ] All payment flows tested end-to-end in sandbox - [ ] Webhook endpoints configured and verified - [ ] Error handling and retry logic implemented - [ ] 3DS redirect flows tested (success and failure) - [ ] CORS origin URLs provided to Inyo team - [ ] Production credentials received and stored securely ## CORS Configuration During onboarding, provide Inyo with your origin URLs for both environments. The tokenizer library (`inyo.js`) communicates with Inyo's servers from the browser, so your domain must be whitelisted per [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. --- description: >- Technical reference for the Inyo Payment Gateway: API keys, base URLs, rate limits, IP allowlisting requirements, and link to the OpenAPI specification. --- # Technical Resources Prior to start integrating and testing, it's necessary to have three components in hand, that needs to be provided by our team. | Key | Sample value | Used for | | :--- | :--- | :--- | | public key | `a23271e1-c1c0-44d3-4567-5610217d1151` | Tokenizing cards using the javascript library | | client id | `MY_CLIENT_ID` | client id to be used during the oauth authentication | | client secret | `4efa3460-a121-1ca9-abcd-e1345ff5510d` | client secret to be used during the oauth authentication | It's also important to take note of the following resources: | Resource | URL | | :--- | :--- | | Static javascript component | [https://cdn.simpleps.com/production/inyo.js](https://cdn.simpleps.com/production/inyo.js) | | OpenAPI specs | [https://sandbox-gw.simpleps.com/q/swagger-ui](https://sandbox-gw.simpleps.com/q/swagger-ui) | | Resource | Sandbox | Production | | :--- | :--- | :--- | | API endpoint | [https://sandbox-gw.simpleps.com/](https://sandbox-gw.simpleps.com/) | [https://gw.simpleps.com/](https://gw.simpleps.com/) | | Postman | Upon request | | Considerations: During the key generation, it will be required by our team the url of the origin server used for development and production. This origin url will ensure that communications in between the browser and our servers are allowed, per [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) rules. #### Rate Limits Each client is allotted a standard amount of requests per minute, that can be reviewed based on specific scenarios. You must implement techniques such as exponential backoff, and be ready to deal with HTTP 429 responses. | Resource | Limit (Requests/Minute) | | :--- | :--- | | Authentication | 60 | | Rates | 200 | | All other authenticated requests | 400 | --- description: >- Protect your business from fraud and chargebacks. Understand the chargeback process, why anti-fraud matters, and best practices for secure payment operations. --- # Mitigating Fraud & Managing Chargebacks ## Why This Matters Every card transaction carries risk. When fraud slips through or a legitimate customer disputes a charge, the result is a **chargeback** — and chargebacks are one of the most expensive problems in payments. Understanding how they work, and how to prevent them, is essential for any business accepting card payments. --- ## What Is a Chargeback? A chargeback is a **forced reversal** of a card transaction, initiated by the cardholder through their issuing bank. Unlike a refund (which you initiate voluntarily), a chargeback is imposed on you — the funds are pulled from your account, and you must prove the transaction was legitimate to get them back. ### How the Chargeback Process Works ``` 1. Cardholder disputes a charge with their bank │ ▼ 2. Issuing bank reviews the claim and files a chargeback │ ▼ 3. Funds are immediately debited from your merchant account + a chargeback fee is applied (typically $15–$100 per case) │ ▼ 4. You receive notification and have a limited window to respond (usually 7–30 days depending on the network) │ ▼ 5. You submit evidence (representment): - Proof of delivery / service rendered - AVS/CVC verification results - 3DS authentication records - Customer communication logs - Signed agreements or terms │ ▼ 6. Card network reviews both sides and makes a ruling │ ┌───┴───┐ ▼ ▼ You win You lose (funds (funds stay with returned) cardholder; you absorb the loss + the fee) ``` ### Why Chargebacks Are So Damaging Chargebacks hurt far more than a simple refund: | Impact | Details | |---|---| | **Financial loss** | You lose the transaction amount + the chargeback fee + the cost of any goods/services already delivered | | **Chargeback ratio** | Card networks track your chargeback rate (chargebacks ÷ total transactions). If it exceeds **1%**, you face penalties, higher processing fees, or account termination | | **Operational cost** | Each dispute requires staff time to gather evidence, prepare documentation, and respond within tight deadlines | | **Reputation** | High chargeback rates can result in being placed on industry monitoring programs (Visa VDMP, Mastercard ECM), making it harder to get processing in the future | ### Common Chargeback Reasons | Category | Examples | |---|---| | **True fraud** | Stolen card used without cardholder's knowledge | | **Friendly fraud** | Cardholder made the purchase but claims they didn't (buyer's remorse, family member used the card) | | **Merchant error** | Wrong amount charged, duplicate charge, product not as described | | **Processing issue** | Charge appeared after cancellation, refund not processed in time | | **Unrecognized charge** | Cardholder doesn't recognize the merchant name on their statement | --- ## The Connection Between Anti-Fraud and Chargebacks Every fraudulent transaction that slips through your defenses is a chargeback waiting to happen. The real cardholder **will** dispute it. And in most fraud-related chargebacks, you lose — because you authorized a transaction with a stolen card. This is why anti-fraud isn't optional. It's directly tied to your bottom line: - **Prevent fraud** → fewer chargebacks → lower fees → keep your merchant account - **Ignore fraud** → chargeback ratio climbs → penalties → potential account termination ### The Pre-Authorization Advantage This is where [pre-authorization](apis/payment/pulling-funds/cards/authorizing/) (`capture: false`) becomes your most powerful tool. When you pre-authorize instead of directly capturing: 1. **Authorize the card** — funds are held, not settled 2. **Review the results** — check AVS, CVC, 3DS outcome, and run your fraud analysis 3. **Decide:** - ✅ Everything looks good → [Capture](apis/payment/pulling-funds/cards/capture.md) the payment - ⚠️ Something is suspicious → [Void](apis/payment/pulling-funds/cards/void.md) immediately — funds are released instantly, no chargeback risk If you had directly captured, you'd need to refund — and if the cardholder files a dispute before your refund processes, you get a chargeback anyway. --- ## Proactive Refunds: Your Best Defense When something goes wrong in the **post-transactional flow** — order can't be fulfilled, product is out of stock, service was not delivered as expected — **refund the customer immediately**. Don't wait for them to call their bank. **Why proactive refunds prevent chargebacks:** | Scenario | Bad outcome | Good outcome | |---|---|---| | Order can't be shipped | Customer waits, gets frustrated, disputes with bank → chargeback | You refund immediately → no dispute | | Product arrives damaged | Customer calls bank first → chargeback + you lose the product | You process a refund on contact → customer satisfied | | Subscription cancelled but still charged | Customer disputes → chargeback + regulatory risk | You refund the erroneous charge → clean resolution | | Duplicate charge | Customer sees two charges, panics, calls bank → two chargebacks | You detect the duplicate, void or refund proactively → zero friction | > **Rule of thumb:** A refund costs you the transaction amount. A chargeback costs you the transaction amount + the fee + staff time + damage to your chargeback ratio. **A voluntary refund is always cheaper than a chargeback.** ### Refund Timing Matters - If the payment is still in **AUTHORIZED** state (not yet captured): [**Void**](apis/payment/pulling-funds/cards/void.md) it. Instant. Clean. The cardholder sees the pending charge disappear. - If the payment is **CAPTURED**: [**Refund**](apis/payment/pulling-funds/cards/refund.md) it. The refund takes 3–10 business days to appear on the cardholder's statement, so communicate with the customer. - **Don't delay:** The longer a charge sits on a customer's statement without resolution, the more likely they are to file a dispute with their bank instead of contacting you. --- ## Anti-Fraud Best Practices ### 1. Use 3D Secure [3DS authentication](apis/payment/pulling-funds/cards/authorizing/handling-3d-secure.md) verifies the cardholder's identity with their bank. In markets where liability shift applies, a fully authenticated 3DS transaction shifts chargeback liability to the issuing bank — meaning you're protected even if fraud occurs. ### 2. Review AVS and CVC Results Every authorization response includes [AVS and CVC verification results](apis/payment/pulling-funds/cards/authorizing/handling-avs-cvc.md). Use them: - **CVC = FAILED** → Strong fraud signal. Consider voiding even if the bank authorized. - **AVS = FAILED** → Address mismatch. Combine with other signals before deciding. - **Both APPROVED** → Lower risk, but not zero. Continue with other checks. ### 3. Implement Velocity Controls Set limits to detect abnormal patterns: - Maximum transactions per card per hour/day - Maximum total amount per card per day - Maximum failed attempts before temporary block - Unusual geographic patterns (card from one country, IP from another) ### 4. Secure Your API Keys - Never expose `clientId` or `secretId` in frontend code, repositories, or logs - Use the [Backend-for-Frontend (BFF) pattern](authentication-methods.md) — all API calls go through your server - Rotate credentials if you suspect compromise ### 5. Never Store Cardholder Data - Use [tokenization](apis/tokenizing-cards.md) for all card operations - Never store PAN, CVV, or expiration dates on your systems - Comply with PCI DSS requirements ### 6. Implement KYC Verify customer identities before processing high-value transactions: - Document verification (ID, passport, driver's license) - Address verification - Phone/email verification - Transaction scoring based on risk factors ### 7. Monitor Transactions Continuously - Set up [webhooks](apis/webhooks.md) to track payment status changes in real-time - Flag unusual patterns for manual review - Track your chargeback ratio daily — intervene before it approaches 1% ### 8. Educate Your Customers - Use a recognizable merchant name on card statements (reduces "unrecognized charge" disputes) - Send clear order confirmations and receipts - Make your refund policy easy to find and understand - Provide accessible customer support — customers who can reach you won't call their bank first ### 9. Keep Records Maintain detailed records for every transaction. If a chargeback does occur, you'll need: - Transaction timestamps and amounts - AVS/CVC/3DS verification results - IP address and device information - Proof of delivery or service rendered - Customer communication history - Signed terms and conditions --- ## Chargeback Response Checklist When you receive a chargeback notification: 1. **Don't ignore it** — You have a limited response window (typically 7–30 days) 2. **Gather evidence** — Pull transaction details, verification results, delivery proof, and communication logs 3. **Assess honestly** — If the customer is right (merchant error, unfulfilled order), accept the chargeback. Fighting a legitimate dispute wastes resources and damages your standing. 4. **Submit representment** — If you have strong evidence the transaction was legitimate, submit it through the chargeback response process 5. **Learn from it** — Every chargeback is a signal. Was it fraud you should have caught? A process gap? A customer service failure? Fix the root cause. --- ## Summary | Prevention Layer | Tools | |---|---| | **Before authorization** | KYC, velocity controls, device fingerprinting | | **During authorization** | 3DS, AVS, CVC, pre-authorization | | **After authorization** | Fraud review, void suspicious pre-auths | | **After capture** | Proactive refunds, clear communication, fast support | | **After dispute** | Evidence gathering, representment, root cause analysis | > **The best chargeback is the one that never happens.** Invest in prevention, use pre-authorization to give yourself a review window, and refund proactively when things go wrong. Your chargeback ratio — and your merchant account — depend on it. --- description: >- Index of all Inyo Payment Gateway API endpoints — card tokenization, payment operations (pull/push/pullpush), webhooks, domain tables, and test data. --- # APIs The Inyo Payment Gateway API provides endpoints for the complete payment lifecycle: | Endpoint | Method | Description | |---|---|---| | `/oauth/token` | POST | [Authenticate](../authentication-methods.md) and obtain an access token | | `/v2/payment` | POST | [Create a payment](payment/pulling-funds/cards/authorizing/) (pull, push, or pullpush) | | `/payments/{id}/capture` | POST | [Capture](payment/pulling-funds/cards/capture.md) a pre-authorized payment | | `/payments/{id}/void` | POST | [Void](payment/pulling-funds/cards/void.md) a pre-authorized payment | | `/payments/{id}/refund` | POST | [Refund](payment/pulling-funds/cards/refund.md) a captured payment | | `/payments/{id}` | GET | [Get payment details](payment/pulling-funds/listing-payments/by-external-id.md) | | `/payments` | GET | [List payments](payment/pulling-funds/listing-payments/get-a-list-of-payments.md) (paginated) | | `/check-card-account` | POST | [Validate a card](check-card-account.md) without charging — AVS, CVC, and Visa ANI name check | | `/foreign-exchange` | POST | [Get FX rate](payment/push-transaction.md#foreign-exchange) for cross-currency pushes | ## Additional Resources - [Tokenizing Cards](tokenizing-cards.md) — Client-side card tokenization with `inyo.js` - [Webhooks](webhooks.md) — Real-time transaction status notifications - [Domain Tables](domain-tables/) — Response codes and payment statuses - [Test Data](test-data/cards.md) — Sandbox test cards and simulation codes --- description: >- Securely tokenize card data client-side using the Inyo JavaScript library. Tokens replace sensitive card details for PCI-compliant payment processing. --- # Tokenizing Cards ## Overview PCI DSS requirements prohibit storing or transmitting raw card data unless you hold the appropriate certification. Inyo's tokenization solution handles this for you: the `inyo.js` library encrypts card data directly in the browser and returns a token that only your API keys can use. **How it works:** 1. Load `inyo.js` in your payment page 2. Add `data-field` attributes to your card input fields 3. Initialize `InyoTokenizer` with your public key and callbacks 4. Call `tokenizeCard()` when the user submits — the library reads the form, encrypts the data, and returns a token 5. Send the token to your backend to create a payment via `POST /v2/payment` ## Domain Whitelisting (Required) For CORS and security reasons, the URL of the page that loads the tokenizer must be registered with Inyo before it can make tokenization requests. This applies to every environment (sandbox and production). - **Before going live**, provide Inyo with the full origin URL (e.g., `https://checkout.yoursite.com`) of every page that will call the tokenizer. - **Any change to the URL** (new domain, subdomain, or port) must be communicated to Inyo so the whitelist can be updated. - Tokenization requests from non-whitelisted origins will be blocked by CORS. If you are using the **PostMessage** integration for 3DS, the URL of the page receiving the `postMessage` events must also be registered with Inyo. This ensures that only your intended page can listen to 3DS authentication results. See [Handling 3D Secure](payment/pulling-funds/cards/authorizing/handling-3d-secure.md) for details. > Contact your Inyo integration manager or [support@inyoglobal.com](mailto:support@inyoglobal.com) to register your URLs. ## Loading the Library Include the tokenizer script with an integrity hash to prevent tampering: ```html ``` | Environment | URL | |---|---| | **Sandbox** | `https://cdn.simpleps.com/sandbox/inyo.js` | | **Production** | `https://cdn.simpleps.com/production/inyo.js` | ## HTML Form Setup Add `data-field` attributes to your card input elements. The tokenizer binds to these automatically — no need to read input values yourself. ```html
``` **Required `data-field` values:** | `data-field` | Input | Notes | |---|---|---| | `cardholder` | Full name on card | As printed on the card | | `pan` | Card number | 13–19 digits | | `expirationDate` | Expiry | Format: `MM/YY` | | `securitycode` | CVV/CVC | 3 digits (Visa/MC/Discover) or 4 digits (Amex) | ## Initializing the Tokenizer Create an `InyoTokenizer` instance when the DOM is ready. The 3DS configuration depends on which integration method you choose — **URL Redirect** or **PostMessage**: ### Example — URL Redirect (traditional) The 3DS provider redirects the browser to your `successUrl` or `failUrl` after authentication. Best for server-rendered apps and full-page checkout flows. ```javascript document.addEventListener('DOMContentLoaded', () => { const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', storeLaterUse: false, threeDSData: { enable: true, successUrl: 'https://yoursite.com/3ds/success', failUrl: 'https://yoursite.com/3ds/fail' }, successCallback: handleSuccess, errorCallback: handleError }); document.getElementById('pay-btn').addEventListener('click', () => { tokenizer.tokenizeCard(); }); }); ``` ### Example — PostMessage (SPA / embedded) The 3DS result is delivered via the browser's `postMessage` API to the parent window. Best for single-page apps and iframe/modal checkout experiences — no page navigation required. ```javascript document.addEventListener('DOMContentLoaded', () => { const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', storeLaterUse: false, threeDSData: { enable: true, enablePostMessage: true }, successCallback: handleSuccess, errorCallback: handleError }); document.getElementById('pay-btn').addEventListener('click', () => { tokenizer.tokenizeCard(); }); }); ``` > When using `enablePostMessage: true`, you do **not** provide `successUrl` or `failUrl`. Instead, you listen for `message` events on the parent window after opening the `redirectAcsUrl` in an iframe. See [Handling 3D Secure](payment/pulling-funds/cards/authorizing/handling-3d-secure.md) for the complete implementation guide. ### Configuration Parameters | Parameter | Type | Required | Description | |---|---|---|---| | `targetId` | string | ✅ | CSS selector of the container holding the `data-field` inputs (e.g., `'#payment-form'`) | | `publicKey` | string | ✅ | Your merchant public key, provided by Inyo | | `successCallback` | function | ✅ | Called when tokenization succeeds | | `errorCallback` | function | ✅ | Called when tokenization fails | | `storeLaterUse` | boolean | ❌ | `false` (default) = one-time token; `true` = recurring/stored token | | `threeDSData` | object | ❌ | 3D Secure configuration (see below) | ### 3DS Configuration (`threeDSData`) | Field | Type | Required | Description | |---|---|---|---| | `enable` | boolean | ✅ | Whether to request 3DS authentication | | `successUrl` | string | ⚠️ | **Redirect mode only.** URL where the 3DS provider redirects on success | | `failUrl` | string | ⚠️ | **Redirect mode only.** URL where the 3DS provider redirects on failure | | `enablePostMessage` | boolean | ⚠️ | **PostMessage mode only.** Set `true` to receive 3DS results via `window.postMessage` instead of URL redirect | > **Choose one mode:** > - **Redirect:** Set `successUrl` + `failUrl`. Do not set `enablePostMessage`. > - **PostMessage:** Set `enablePostMessage: true`. Do not set `successUrl` / `failUrl`. > > Enabling 3DS does not guarantee a challenge will occur — the issuing bank decides based on its risk assessment. See [Handling 3D Secure](payment/pulling-funds/cards/authorizing/handling-3d-secure.md) for the full flow and code examples for both modes. ## Handling Callbacks ### Success Callback ```javascript function handleSuccess(response) { console.log('Tokenization response:', response); if (response.reasonCode === 'WAITING_TRANSACTION') { // Token created — extract it const token = response.additionalData.token; const lastFour = response.additionalData.lastFour; console.log(`Card ending in ${lastFour}, token: ${token}`); // Send the token to your backend server // Your backend will call POST /v2/payment with this token submitPaymentToBackend(token); } else { console.warn('Unexpected response step:', response.step); } } ``` **Success response fields:** | Field | Description | |---|---| | `reasonCode` | `"WAITING_TRANSACTION"` when token is ready | | `additionalData.token` | The card token UUID — use as `cardTokenId` in payment requests | | `additionalData.lastFour` | Last 4 digits of the card number (for display) | ### Error Callback ```javascript function handleError(response) { console.error('Tokenization error:', response); // Clear previous error states document.querySelectorAll('#cc-number, #cc-expiration, #cc-cvv') .forEach(el => el.classList.remove('is-invalid')); // Highlight the specific field that failed switch (response.code) { case 'INVALID_PAN': document.querySelector('#cc-number').classList.add('is-invalid'); break; case 'INVALID_EXPIRY_DATE': document.querySelector('#cc-expiration').classList.add('is-invalid'); break; case 'INVALID_CVV': document.querySelector('#cc-cvv').classList.add('is-invalid'); break; default: console.error('Unknown error code:', response.code); } } ``` **Error codes:** | Code | Description | |---|---| | `INVALID_PAN` | Card number is invalid (failed Luhn check or unsupported scheme) | | `INVALID_EXPIRY_DATE` | Expiration date is invalid or card is expired | | `INVALID_CVV` | Security code is invalid | ## One-Time vs. Recurring Tokens | Token Type | `storeLaterUse` | Usage | |---|---|---| | **One-time** | `false` | Single transaction only — cannot be reused | | **Recurring** | `true` | Can be stored and reused for future charges | **Rules:** - You **must not** store one-time tokens after use - You **may** store recurring tokens server-side for future transactions - You **must never** store raw card data (PAN, CVV, expiration) regardless of token type - When using a stored recurring token for a subsequent payment, pass the `previousPaymentId` (from the original authorization) alongside the `cardTokenId` ## Complete Examples ### Example A — URL Redirect (server-rendered checkout) ```html Inyo Payment — Redirect
``` ### Example B — PostMessage (SPA / embedded checkout) ```html Inyo Payment — PostMessage
``` ## Next Steps With a token in hand, proceed to [Authorizing a Card Payment](payment/pulling-funds/cards/authorizing/) to create the transaction via `POST /v2/payment`.
--- description: >- Validate a card without charging it using the Check Card Account API. Returns AVS and CVC results for all cards, plus Visa ANI cardholder name verification for Visa cards. --- # Check Card Account (ANI) The Check Card Account API validates a card without processing a payment. It performs up to three verification checks and returns the results: - **AVS** — Address Verification Service (billing address match) — all card networks - **CVC** — Card Verification Code (security code match) — all card networks - **AAV Cardholder Name** — Visa [Account Name Inquiry](https://corporate.visa.com/content/dam/VCOM/regional/na/us/support-legal/documents/account-name-inquiry-onesheet-merchant-version.pdf) (cardholder name match against issuer records) — **Visa cards only** AVS and CVC verification are standard checks performed for any card. The cardholder name check is powered by Visa's **Account Name Inquiry (ANI)** product, which queries the issuing bank's records to verify that the name submitted matches the legal name on the account. ANI is only available for Visa cards — for non-Visa cards, the `aavCardholderNameResult` field will return `N/A`. ## What is Visa ANI? [Account Name Inquiry (ANI)](https://corporate.visa.com/content/dam/VCOM/regional/na/us/support-legal/documents/account-name-inquiry-onesheet-merchant-version.pdf) is a Visa product that enables merchants to verify the cardholder's name directly with the issuing bank before or independently of a financial transaction. When a card is submitted, Visa forwards the provided first, middle, and last name to the issuer, who compares them against the legal name on the account and returns a match result. Key details about Visa ANI: - **Visa only** — ANI is a Visa network service. It is not available for Mastercard, Amex, Discover, or other networks. - **Issuer participation** — ANI became mandatory for U.S. and Canadian Visa issuers in October 2023, meaning most Visa issuers in these markets support it. Some issuers outside these markets may not yet support ANI, in which case the result will be `N/A`. - **Name matching** — Visa checks first, middle, and last name components separately against the issuer's records. At minimum, the last name must be provided. The issuer returns the closest match when multiple names are on file (e.g., joint accounts). - **Independent of payment** — ANI operates independently of the financial transaction. No funds are held or moved. - **Exclusions** — Business/corporate cards and non-reloadable prepaid cards without a registered name are not supported for ANI. ## Use Cases - **Onboarding** — Validate a customer's card before storing it for future charges - **Fraud prevention** — Confirm the person submitting the card is the actual cardholder - **KYC enhancement** — Cross-reference the name on the card with the name provided during identity verification - **Card-on-file verification** — Check that a stored card is still valid and belongs to the expected person ## How It Works ``` 1. Tokenize card 2. POST /check-card-account 3. Read results (client-side) (server-side) │ │ For all cards: ▼ ▼ → AVS result inyo.js encrypts Gateway verifies AVS + CVC → CVC result card + name → for all cards. If Visa, returns cardTokenId also queries ANI for For Visa cards only: cardholder name match → → ANI name result ``` > **Note:** This API does **not** charge the card or create a payment. It is purely a validation call. No amount is required in the request. --- ## Endpoint ``` POST https://sandbox-gw.simpleps.com/check-card-account ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Body The payload is simplified compared to standard payment requests — no `amount`, `paymentType`, or `capture` fields are needed. ### Root Object | Field | Type | Required | Description | |---|---|---|---| | `externalPaymentId` | string | ✅ | Your unique identifier for this validation request (idempotency key) | | `ipAddress` | string | ✅ | Requester's IPv4 or IPv6 address | | `sender` | object | ✅ | Cardholder details | ### `sender` Object | Field | Type | Required | Description | |---|---|---|---| | `firstName` | string | ✅ | Cardholder's first name (compared against issuer records via Visa ANI for Visa cards) | | `lastName` | string | ✅ | Cardholder's last name (compared against issuer records via Visa ANI for Visa cards) | | `email` | string | ❌ | Cardholder's email address | | `address` | object | ❌ | Billing address (used for AVS verification) | | `paymentMethod` | object | ✅ | Tokenized card details | ### `sender.address` Object | Field | Type | Required | Description | |---|---|---|---| | `countryCode` | string | ✅ | ISO Alpha-3 country code (e.g., `"USA"`) | | `stateCode` | string | ✅ | State/province abbreviation (e.g., `"NY"`) | | `city` | string | ✅ | City name | | `line1` | string | ✅ | Street address line 1 | | `line2` | string | ❌ | Street address line 2 | | `zipCode` | string | ✅ | Postal/ZIP code | ### `sender.paymentMethod` Object | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"CARD"` | | `cardTokenId` | string | ✅ | Token UUID from the [tokenizer](tokenizing-cards.md) | ## Example Request ```bash curl -X POST https://sandbox-gw.simpleps.com/check-card-account \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "card-check-001", "ipAddress": "203.0.113.42", "sender": { "firstName": "John", "lastName": "Smith", "email": "john.smith@example.com", "address": { "countryCode": "US", "stateCode": "NY", "city": "New York", "line1": "123 Main Street", "zipCode": "10001" }, "paymentMethod": { "type": "CARD", "cardTokenId": "ab5fc589-8b48-4531-94c0-68b0629c13fe" } } }' ``` ## Response ### Approved (200) All three verification checks passed: ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "status": "APPROVED", "avsResult": "APPROVED", "cvcResult": "APPROVED", "aavCardholderNameResult": "APPROVED", "reasonCode": "00" } ``` ### Rejected (200) One or more checks failed — the card or identity could not be verified: ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "status": "REJECTED", "avsResult": "APPROVED", "cvcResult": "APPROVED", "aavCardholderNameResult": "FAILED", "reasonCode": "PAY_084" } ``` ### 3DS Challenge (200) The issuing bank requires cardholder authentication before returning results: ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "status": "CHALLENGE", "redirectAcsUrl": "https://sandbox-gw.simpleps.com/secure-code/start-challenge?token=dce568c6-...", "avsResult": "N/A", "cvcResult": "N/A", "aavCardholderNameResult": "N/A" } ``` When you receive `CHALLENGE`, redirect the cardholder to `redirectAcsUrl`. See [Handling 3D Secure](payment/pulling-funds/cards/authorizing/handling-3d-secure.md) for the complete flow. ## Response Fields | Field | Type | Description | |---|---|---| | `paymentId` | string | Inyo's unique identifier for this validation | | `status` | string | `APPROVED`, `REJECTED`, or `CHALLENGE` | | `redirectAcsUrl` | string | 3DS challenge URL (only present when `status` = `CHALLENGE`) | | `avsResult` | string | Address verification result (see below) | | `cvcResult` | string | Card verification code result (see below) | | `aavCardholderNameResult` | string | Cardholder name verification result (see below) | | `reasonCode` | string | Response code (see [Response Codes](domain-tables/response-code.md)) | ## Understanding the Verification Results ### AVS Result (`avsResult`) Compares the billing address provided in the request with the address on file at the card issuer. | Value | Meaning | Action | |---|---|---| | `APPROVED` | Address matches issuer records | ✅ Low fraud risk | | `FAILED` | Address does not match | ⚠️ Elevated fraud risk | | `NOT_SENT` | No billing address was provided | ℹ️ No verification performed | | `N/A` | Not applicable (e.g., 3DS challenge pending) | ℹ️ Check after challenge completes | ### CVC Result (`cvcResult`) Validates the 3 or 4-digit security code printed on the card. | Value | Meaning | Action | |---|---|---| | `APPROVED` | CVC matches issuer records | ✅ Card in possession | | `FAILED` | CVC does not match | ⚠️ High fraud risk | | `NOT_SENT` | CVC was not provided | ℹ️ No verification performed | | `N/A` | Not applicable | ℹ️ Check after challenge completes | ### AAV Cardholder Name Result (`aavCardholderNameResult`) **Visa cards only.** Verifies whether the `firstName` and `lastName` in the request match the legal name registered with the issuing bank. This is powered by Visa's [Account Name Inquiry (ANI)](https://corporate.visa.com/content/dam/VCOM/regional/na/us/support-legal/documents/account-name-inquiry-onesheet-merchant-version.pdf) service. | Value | Meaning | Action | |---|---|---| | `APPROVED` | Name matches issuer records | ✅ Confirmed cardholder identity | | `FAILED` | Name does not match | ⚠️ Possible identity mismatch — investigate | | `N/A` | Not available — card is not Visa, issuer does not support ANI, or 3DS pending | ℹ️ Fall back to AVS and CVC results | > **Important:** ANI is only performed for **Visa cards**. For Mastercard, Amex, Discover, and other networks, `aavCardholderNameResult` will always return `N/A`. In these cases, rely on AVS and CVC for verification. > **Name formatting:** The cardholder name comparison is performed by the Visa issuer, which checks first, middle, and last name components separately. Ensure you pass the name **exactly as it appears on the card**. Visa supports up to 35 characters per name field. Suffixes (Jr., III) and prefixes (Dr.) are not supported by ANI and should be omitted. ## Integration Example ### Step 1 — Tokenize the Card Use `inyo.js` to collect and tokenize the card data client-side. The cardholder name entered in the `data-field="cardholder"` input is included in the token. ```javascript const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', storeLaterUse: false, threeDSData: { enable: true, enablePostMessage: true }, successCallback: (response) => { if (response.reasonCode === 'WAITING_TRANSACTION') { checkCardAccount(response.additionalData.token); } }, errorCallback: (err) => console.error('Tokenization failed:', err) }); ``` ### Step 2 — Call Check Card Account Send the token to your backend, which calls the Check Card Account API: ```javascript async function checkCardAccount(cardTokenId) { const response = await fetch('https://sandbox-gw.simpleps.com/check-card-account', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ externalPaymentId: crypto.randomUUID(), ipAddress: customerIpAddress, sender: { firstName: 'John', lastName: 'Smith', email: 'john.smith@example.com', paymentMethod: { type: 'CARD', cardTokenId: cardTokenId } } }) }); const result = await response.json(); if (result.status === 'CHALLENGE' && result.redirectAcsUrl) { // Handle 3DS — redirect or open iframe window.open(result.redirectAcsUrl, '_blank'); return; } // Evaluate all three verification signals console.log('AVS:', result.avsResult); console.log('CVC:', result.cvcResult); console.log('ANI (Name):', result.aavCardholderNameResult); } ``` ### Step 3 — Evaluate Results Combine all signals to make a decision. Remember that `aavCardholderNameResult` is only meaningful for Visa cards — for other networks it will be `N/A`. ```javascript function evaluateCardCheck(result) { if (result.status === 'REJECTED') { return { accept: false, reason: 'Card validation rejected' }; } // CVC mismatch — card not in possession if (result.cvcResult === 'FAILED') { return { accept: false, reason: 'Invalid security code' }; } // Visa ANI: name mismatch — high risk // (N/A means non-Visa card or issuer doesn't support ANI — not a failure) if (result.aavCardholderNameResult === 'FAILED') { return { accept: false, reason: 'Cardholder name does not match issuer records (Visa ANI)' }; } // All available checks passed if (result.avsResult === 'APPROVED' && result.cvcResult === 'APPROVED') { const aniStatus = result.aavCardholderNameResult === 'N/A' ? 'not available (non-Visa or unsupported issuer)' : 'name verified'; return { accept: true, reason: `AVS + CVC passed; ANI: ${aniStatus}` }; } // Partial results — use your own risk logic return { accept: true, reason: 'Partial verification — review recommended' }; } ``` ## Testing Use the following test card in sandbox for Check Card Account: | Card Number | Network | Description | |---|---|---| | `4462 0300 0000 0000` | Visa Debit | Standard ANI test card | Use the cardholder name `AUTHORISED` to simulate an approved result. See [Test Data — Cards](test-data/cards.md) for CVV and AVS simulation values. ## Best Practices 1. **Always send the billing address** — Including the address enables AVS verification alongside CVC (and ANI for Visa), giving you multiple independent fraud signals regardless of card network. 2. **Match the tokenizer name to the API name** — The `cardholder` field in the tokenizer form should contain the same name you pass as `sender.firstName` and `sender.lastName`. For Visa ANI, name mismatches between the tokenized name and the API name will cause the issuer to return a no-match result. 3. **Don't rely on a single signal** — Evaluate AVS + CVC together for all cards, and add the ANI result for Visa. A single `FAILED` result doesn't necessarily mean fraud, but multiple failures are a strong indicator. 4. **Handle `N/A` gracefully** — The `aavCardholderNameResult` field will return `N/A` for all non-Visa cards, and for Visa cards from issuers that don't yet support ANI. This is expected behavior — fall back to AVS and CVC results for your fraud decision. 5. **Use before storing cards** — Run a Check Card Account call before saving a card token with `storeLaterUse: true` to confirm the card is valid and belongs to the expected person. 6. **Use the legal name on the card** — Visa ANI compares against the legal name the issuer has on file, not nicknames or preferred names. Omit suffixes (Jr., III) and prefixes (Dr.) as they are not supported by Visa ANI. ## What's Next - [Tokenizing Cards](tokenizing-cards.md) — Set up client-side card tokenization - [Handling AVS / CVC](payment/pulling-funds/cards/authorizing/handling-avs-cvc.md) — Deep dive into address and security code verification - [Handling 3D Secure](payment/pulling-funds/cards/authorizing/handling-3d-secure.md) — Handle CHALLENGE responses - [Authorizing a Card Payment](payment/pulling-funds/cards/authorizing/) — Create actual payment transactions after validation --- description: >- Payment API overview — create pull (collect), push (payout), and combined pull+push transactions using the POST /v2/payment endpoint. --- # Payment The Inyo Gateway supports three payment operation types: | Type | `paymentType` | Description | |---|---|---| | **Pull** | `"PULL"` | [Collect funds](pulling-funds/) from a customer (cards, ACH) | | **Push** | `"PUSH"` | [Send funds](push-transaction.md) to a recipient (cards, PIX, wallets, bank accounts) | | **Pull + Push** | `"PULLPUSH"` | [Collect and send](pull-and-push-in-one-step.md) in a single API call | All payment types use the same endpoint: ``` POST /v2/payment ``` The `paymentType` field in the request body determines the operation. --- description: >- Pull transactions collect funds from a customer's card or bank account. Supports credit/debit cards and ACH transfers. --- # Pulling Funds Pull (collect) transactions withdraw funds from a customer's payment method. The Inyo Gateway supports: - **Credit/Debit Cards** — Via tokenized card data (see [Tokenizing Cards](../../tokenizing-cards.md)) - **ACH Bank Transfers** — Via bank account and routing number All pull transactions use the same endpoint: ``` POST https://sandbox-gw.simpleps.com/v2/payment ``` ## Request Structure ### Root Object | Field | Type | Required | Description | |---|---|---|---| | `externalPaymentId` | string | ✅ | Your unique payment identifier (idempotency key) | | `ipAddress` | string | ✅ | Payer's IPv4 or IPv6 address | | `paymentType` | string | ✅ | `"PULL"` | | `capture` | boolean | ✅ | `true` = auto-capture; `false` = pre-auth only (cards only) | | `amount` | object | ✅ | Transaction amount | | `sender` | object | ✅ | Payer information, address, and payment method | ### `amount` Object | Field | Type | Required | Description | |---|---|---|---| | `total` | number | ✅ | Payment amount (must be ≥ 1) | | `currency` | string | ✅ | ISO 4217 currency code (e.g., `"USD"`) | ### `sender` Object | Field | Type | Required | Description | |---|---|---|---| | `firstName` | string | ✅ | Payer's first name | | `lastName` | string | ✅ | Payer's last name | | `address` | object | ✅ | Billing address | | `paymentMethod` | object | ✅ | Card or bank account details | ### `sender.address` Object | Field | Type | Required | Description | |---|---|---|---| | `countryCode` | string | ✅ | ISO Alpha-2 country code (e.g., `"US"`) | | `stateCode` | string | ✅ | State abbreviation (e.g., `"NY"`) | | `city` | string | ✅ | City name | | `line1` | string | ✅ | Street address line 1 | | `line2` | string | ❌ | Street address line 2 | | `zipCode` | string | ✅ | Postal/ZIP code | ### `sender.paymentMethod` — Card | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"CARD"` | | `cardTokenId` | string | ✅ | Token UUID from the tokenizer | | `previousPaymentId` | string | ❌ | For recurring tokens: `paymentId` from the initial authorization | ### `sender.paymentMethod` — Bank Account (ACH) | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"BANK_DEPOSIT"` | | `accountNumber` | string | ✅ | Bank account number (6–20 digits) | | `routingNumber` | string | ✅ | ABA routing number (9 digits) | | `accountType` | string | ✅ | `"CHECKING"`, `"SAVINGS"`, `"BUSINESS_CHECKING"`, or `"BUSINESS_SAVINGS"` | ## Card Payment Lifecycle ``` Tokenize → Authorize → (3DS Challenge?) → Capture → (Refund?) ↘ Void ``` For card payments: 1. [**Authorize**](cards/authorizing/) — Create the payment (`"capture": false` for pre-auth) 2. [**Handle 3DS**](cards/authorizing/handling-3d-secure.md) — If `status` = `CHALLENGE`, redirect to `redirectAcsUrl` 3. [**Capture**](cards/capture.md) — Settle the authorized payment (within 7 days) 4. [**Void**](cards/void.md) — Cancel before capture 5. [**Refund**](cards/refund.md) — Return funds after capture (full or partial) ## ACH Payments ACH payments are always captured immediately (`"capture": true`). The pre-auth/capture lifecycle does not apply. See [ACH (Bank Account)](ach-bank-account.md) for a complete example. ## What's Next | Guide | Description | |---|---| | [Card Authorization](cards/authorizing/) | Full card payment flow with examples | | [ACH Payment](ach-bank-account.md) | Bank account pull with example payload | | [3D Secure](cards/authorizing/handling-3d-secure.md) | Challenge redirect handling | | [AVS / CVC](cards/authorizing/handling-avs-cvc.md) | Address and security code verification | | [Capture](cards/capture.md) | Settle a pre-authorized payment | | [Void](cards/void.md) | Cancel before settlement | | [Refund](cards/refund.md) | Return funds after capture | --- description: >- Card payment operations: authorize a card token, handle 3DS challenges, capture authorized funds, and issue refunds or voids. --- # Card Payments Card payments follow a lifecycle: **Tokenize → Authorize → (3DS?) → Capture → (Refund?)** | Step | Guide | |---|---| | 1. Tokenize | [Tokenizing Cards](../../../tokenizing-cards.md) | | 2. Authorize | [Authorizing a Card Payment](authorizing/) | | 3. Handle 3DS | [Handling 3D Secure](authorizing/handling-3d-secure.md) | | 4. Capture | [Capture](capture.md) | | 5. Void | [Void](void.md) (before capture) | | 6. Refund | [Refund](refund.md) (after capture) | For AVS and CVC verification details, see [Handling AVS / CVC](authorizing/handling-avs-cvc.md). --- description: >- Authorize a card payment by submitting a tokenized card to the /v2/payment endpoint. Supports pre-authorization and direct capture. --- # Authorizing a Card Payment After [tokenizing a card](../../../../tokenizing-cards.md), submit the token to create a payment authorization. You can choose between: - **Pre-authorization** (`"capture": false`) — Reserves funds without settling. You capture later when ready. - **Direct capture** (`"capture": true`) — Authorizes and captures in one step. Funds are settled immediately. --- ## Understanding Pre-Authorization vs. Direct Capture This is one of the most commonly misunderstood concepts in card payments. Getting it right determines how much control you have over your money flow — and how quickly you can reverse a transaction if something goes wrong. ### What is pre-authorization? When you set `"capture": false`, the gateway asks the cardholder's bank to **reserve** the funds on their card — but **no money moves yet**. The cardholder sees a "pending" charge on their statement, but settlement (the actual transfer of funds to your account) does not happen until you explicitly [capture](../capture.md) the payment. Think of it as placing a hold on the funds. The money is still in the cardholder's account, but they can't spend it elsewhere. ### Why use pre-authorization? Pre-authorization gives you a window to perform additional checks **before** committing to the financial settlement: - **AVS / CVC verification** — Review the address and security code results from the authorization response. If they indicate fraud risk, you can cancel immediately. - **Anti-fraud analysis** — Run the transaction through your fraud scoring system, manual review queues, or third-party fraud tools. - **Inventory / fulfillment checks** — Confirm the item is in stock, the service is available, or any business-specific validation passes. - **Compliance / KYC** — Verify the customer meets regulatory requirements before finalizing the transaction. - **Order adjustments** — The final amount may differ from the initial hold (e.g., partial shipments). You can capture a smaller amount than authorized. ### The key advantage: fast, clean reversal If you need to cancel a pre-authorized transaction, you perform a [**void**](../void.md). A void **instantly releases** the hold on the cardholder's funds — typically within minutes. The cardholder sees the pending charge disappear from their statement. No money was ever moved, so there's nothing to "return." Compare this to what happens after capture: once a transaction is captured, settlement has occurred. The money has left the cardholder's account and landed in yours. The **only** way to return funds at that point is a [**refund**](../refund.md), which is a separate financial transaction that can take **3–10 business days** to appear on the cardholder's statement. ### Side-by-side comparison | | Pre-authorization (`capture: false`) | Direct capture (`capture: true`) | |---|---|---| | **What happens** | Funds are held (reserved) on the card | Funds are held AND settled immediately | | **Money moves?** | ❌ No — just a hold | ✅ Yes — settlement begins | | **To cancel** | **Void** — instant, no money moved | **Refund** — separate transaction, 3–10 days | | **Cardholder sees** | "Pending" charge | Completed charge | | **Time limit** | Must capture within **7 days** or the hold expires automatically | N/A — already captured | | **Can adjust amount?** | ✅ Capture less than authorized | ❌ Amount is final | | **Best for** | E-commerce, travel, subscriptions, anything needing review | Simple point-of-sale, instant digital goods | ### When to use which **Use pre-authorization (`capture: false`) when:** - You need time to verify fraud signals, AVS/CVC results, or compliance - The final amount might change (partial shipments, tips, hotel incidentals) - You want the ability to cancel cleanly without a refund hitting the cardholder's statement - Your fulfillment has any delay between payment and delivery **Use direct capture (`capture: true`) when:** - The transaction is final and immediate (digital download, in-store purchase) - No review or adjustment is needed - You want the simplest integration with fewer API calls ### The lifecycle at a glance ``` capture: false capture: true ───────────── ───────────── POST /v2/payment POST /v2/payment │ │ ▼ ▼ AUTHORIZED CAPTURED (funds held) (funds settled) │ │ ┌───┴───┐ ▼ │ │ REFUNDED ▼ ▼ (3-10 days to CAPTURED VOIDED cardholder) (settle) (release hold, │ instant) ▼ REFUNDED (3-10 days) ``` > **Bottom line:** If there's any chance you might need to cancel the transaction after authorization, use pre-authorization. It's faster to reverse, cleaner for the cardholder, and gives you full control over the settlement timing. --- ## Endpoint ``` POST https://sandbox-gw.simpleps.com/v2/payment ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Body ### Root Object | Field | Type | Required | Description | |---|---|---|---| | `externalPaymentId` | string | ✅ | Your unique identifier for this payment (idempotency key) | | `ipAddress` | string | ✅ | Payer's IPv4 or IPv6 address | | `paymentType` | string | ✅ | `"PULL"` | | `capture` | boolean | ✅ | `true` = auto-capture; `false` = pre-auth only | | `amount` | object | ✅ | Transaction amount | | `sender` | object | ✅ | Payer details | ### `amount` Object | Field | Type | Required | Description | |---|---|---|---| | `total` | number | ✅ | Amount to charge (must be ≥ 1) | | `currency` | string | ✅ | ISO 4217 currency code (e.g., `"USD"`) | ### `sender` Object | Field | Type | Required | Description | |---|---|---|---| | `firstName` | string | ✅ | Payer's first name | | `lastName` | string | ✅ | Payer's last name | | `address` | object | ✅ | Billing address (used for AVS verification) | | `paymentMethod` | object | ✅ | Card token details | ### `sender.address` Object | Field | Type | Required | Description | |---|---|---|---| | `countryCode` | string | ✅ | ISO Alpha-3 country code (e.g., `"USA"`) | | `stateCode` | string | ✅ | State/province abbreviation (e.g., `"NY"`) | | `city` | string | ✅ | City name | | `line1` | string | ✅ | Street address line 1 | | `line2` | string | ❌ | Street address line 2 | | `zipCode` | string | ✅ | Postal/ZIP code | ### `sender.paymentMethod` Object (Card) | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"CARD"` | | `cardTokenId` | string | ✅ | Token UUID from the tokenizer | | `previousPaymentId` | string | ❌ | Required for recurring tokens — the `paymentId` from the initial authorization | ## Example Request ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "order-12345", "ipAddress": "203.0.113.42", "paymentType": "PULL", "capture": false, "amount": { "total": 99.99, "currency": "USD" }, "sender": { "firstName": "John", "lastName": "Smith", "address": { "countryCode": "US", "stateCode": "NY", "city": "New York", "line1": "123 Main Street", "line2": "Apt 4B", "zipCode": "10001" }, "paymentMethod": { "type": "CARD", "cardTokenId": "ab5fc589-8b48-4531-94c0-68b0629c13fe" } } }' ``` ## Response ### Successful Authorization (200) ```json { "status": 200, "data": { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "parentPaymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": 99.99, "created": "2025-03-31 03:49:05", "approved": true, "message": "Payment Approved", "automaticReversed": false, "status": "AUTHORIZED", "captured": false, "voided": false, "responseCode": "00", "issuerName": "BANK OF AMERICA", "issuerCountry": "UNITED STATES", "cvcResult": "APPROVED", "avsResult": "APPROVED", "avsCardholderNameResult": "N/A", "avsTelephoneResult": "N/A" } } ``` ### 3DS Challenge Required (200) When the issuing bank requires cardholder verification, the response returns `status: "CHALLENGE"` with a `redirectAcsUrl`: ```json { "status": 200, "data": { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "redirectAcsUrl": "https://sandbox-gw.simpleps.com/secure-code/start-challenge?token=dce568c6-...", "amount": 99.99, "approved": false, "message": "Payment awaiting 3DS challenge verification", "status": "CHALLENGE", "captured": false, "voided": false, "responseCode": "00", "cvcResult": "N/A", "avsResult": "N/A" } } ``` When you receive `CHALLENGE`, redirect the cardholder to `redirectAcsUrl`. See [Handling 3D Secure](handling-3d-secure.md) for the complete flow. ### Declined (400) ```json { "status": 400, "data": { "paymentId": "abc12345-...", "externalPaymentId": "order-12345", "amount": 99.99, "approved": false, "message": "Not sufficient funds", "status": "DECLINED", "responseCode": "PAY_084" } } ``` ## Response Fields | Field | Type | Description | |---|---|---| | `paymentId` | string | Inyo's unique payment identifier | | `parentPaymentId` | string | Parent payment ID (same as `paymentId` for initial authorizations) | | `externalPaymentId` | string | Your original external ID | | `redirectAcsUrl` | string | 3DS challenge URL (only present when `status` = `CHALLENGE`) | | `amount` | number | Transaction amount | | `created` | string | Timestamp (EST) | | `approved` | boolean | `true` if authorized successfully | | `message` | string | Human-readable status message | | `automaticReversed` | boolean | `true` if payment was auto-reversed by fraud rules | | `status` | string | `AUTHORIZED`, `CHALLENGE`, or `DECLINED` | | `captured` | boolean | `true` if auto-captured (`"capture": true` in request) | | `voided` | boolean | `true` if voided | | `responseCode` | string | Issuer/gateway response code (see [Response Codes](../../../../domain-tables/response-code.md)) | | `issuerName` | string | Name of the issuing bank | | `issuerCountry` | string | Country of the issuer | | `cvcResult` | string | `APPROVED`, `FAILED`, `NOT_SENT`, or `N/A` | | `avsResult` | string | `APPROVED`, `FAILED`, `NOT_SENT`, or `N/A` | ## Using a Recurring Token If the card was tokenized with `storeLaterUse: true`, you can reuse the token for future charges by including the `previousPaymentId`: ```json { "sender": { "paymentMethod": { "type": "CARD", "cardTokenId": "ab5fc589-8b48-4531-94c0-68b0629c13fe", "previousPaymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8" } } } ``` ## What's Next - **Authorized?** → [Capture](../capture.md) the payment when ready to settle - **Need to cancel?** → [Void](../void.md) the authorization before capture - **3DS Challenge?** → [Handle 3D Secure](handling-3d-secure.md) redirect flow - **Check verification?** → [AVS / CVC results](handling-avs-cvc.md) --- description: >- Understand AVS and CVC verification results to assess fraud risk on card payments. --- # Handling AVS / CVC Address Verification System (AVS) and Card Verification Code (CVC/CVV) checks are fraud prevention tools that compare customer-provided data against the card issuer's records. ## How It Works When you include the `sender.address` (billing address) in your payment request, the Inyo Gateway forwards it to the card network for verification. The results are returned in the payment response. > **Note:** AVS and CVC results do **not** directly control whether a payment is approved or declined. The issuing bank may authorize a payment even with mismatched AVS/CVC. These results are advisory — use them as signals in your own fraud decision logic. ## Response Fields Every payment response includes these verification fields: ```json { "cvcResult": "APPROVED", "avsResult": "APPROVED", "avsCardholderNameResult": "N/A", "avsTelephoneResult": "N/A" } ``` ## CVC Verification (`cvcResult`) CVC verifies the 3 or 4 digit security code on the card. | Status | Meaning | Action | |---|---|---| | `APPROVED` | CVV matches issuer records | ✅ Low fraud risk | | `FAILED` | CVV does not match | ⚠️ High fraud risk — consider voiding even if authorized | | `NOT_SENT` | CVV was not provided in the request | ℹ️ No verification performed | | `N/A` | Not applicable (e.g., during 3DS challenge) | ℹ️ Check after challenge completes | ## AVS Verification (`avsResult`) AVS compares the billing address (house number, street, and postal code) against the issuer's records. | Status | Meaning | Action | |---|---|---| | `APPROVED` | Address components match | ✅ Low fraud risk | | `FAILED` | Address components don't match | ⚠️ Elevated fraud risk | | `NOT_SENT` | No billing address was provided | ℹ️ No verification performed | | `N/A` | Not applicable (e.g., 3DS challenge pending) | ℹ️ Check after authorization | ## Configuration Options During merchant onboarding, you can configure AVS behavior at the Inyo level: 1. **Inyo-managed** — Inyo automatically evaluates AVS results and may decline or reverse transactions that fail verification, based on rules you define together. 2. **Self-managed** — Inyo returns the raw AVS/CVC results and your system decides how to handle them (e.g., void transactions with failed CVC). Contact the Inyo team to configure your preferred approach. ## Best Practices 1. **Always send billing address** — Even if AVS is advisory, providing address data improves authorization rates and gives you fraud signals. 2. **Combine signals** — Don't rely on a single check. Evaluate CVC + AVS + 3DS results together with transaction context (amount, velocity, device fingerprint). 3. **Void suspicious authorizations** — If a payment is authorized but CVC = `FAILED`, consider [voiding](../void.md) it before capture rather than accepting the risk. 4. **Log results** — Store AVS/CVC results for chargeback disputes. Demonstrating you verified the cardholder's identity strengthens your case. ## Testing AVS / CVC Use special values in sandbox to simulate different verification outcomes. See [Test Data — Cards](../../../../test-data/cards.md) for the full table. **CVC test values (Visa/MC/Discover):** | CVV Value | Result | |---|---| | `555` | Matched (APPROVED) | | `444` | Not matched (FAILED) | | _(empty)_ | Not supplied | **AVS test values (via zip code):** | Zip Code | Result | |---|---| | `AAAAA` | Full match (APPROVED) | | `JJJJJ` | No match (FAILED) | | _(empty)_ | Not supplied | --- description: >- Handle 3D Secure (3DS) authentication for card payments. Two integration options: URL redirect or JavaScript postMessage. --- # Handling 3D Secure 3D Secure (3DS) adds a layer of cardholder authentication to reduce fraud. When 3DS is triggered, the cardholder may need to verify their identity with their issuing bank before the payment is authorized. ## 3DS Modes | Mode | Cardholder Interaction | Description | |---|---|---| | **Data-only** | None | Transaction data is sent to the issuer for risk assessment; no cardholder action needed | | **Challenge** | Required | Cardholder verifies via OTP, biometrics, or bank app | > **Important:** In the US market, banks are not mandated to enforce step-up authentication. The issuing bank decides whether to issue a challenge, even if you request one during tokenization. 3DS in the AFT (Account Funding Transaction) market is for fraud control only — there is no liability shift. ## Enabling 3DS 3DS can be enabled in two ways: 1. **Automatic** (backend configuration) — All payments require 3DS. Configured by the Inyo team during onboarding. 2. **Manual** (per-transaction) — You control when to request 3DS by setting `threeDSData` during [card tokenization](../../../../tokenizing-cards.md). ## Integration Options When the payment API returns `status: "CHALLENGE"`, the cardholder must complete authentication. You have **two ways** to handle the challenge result: | Option | How it works | Best for | |---|---|---| | **URL Redirect** | Browser redirects to `redirectAcsUrl`; after authentication, the 3DS provider redirects back to your `successUrl` or `failUrl` | Full-page checkout flows, server-rendered apps | | **PostMessage** | Open `redirectAcsUrl` in an iframe; the 3DS provider sends a `postMessage` event to the parent window with the result | Single-page apps (SPAs), embedded/modal checkout experiences | --- ## Option 1: URL Redirect The traditional approach. The cardholder's browser navigates to the bank's 3DS page, and after authentication, is redirected back to your site. ### Tokenizer Configuration ```javascript const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', threeDSData: { enable: true, successUrl: 'https://yoursite.com/3ds/success', failUrl: 'https://yoursite.com/3ds/fail' }, successCallback: handleSuccess, errorCallback: handleError }); ``` ### Flow ``` 1. Frontend tokenizes card → receives token 2. Backend calls POST /v2/payment with token 3. API returns status: "CHALLENGE" + redirectAcsUrl 4. Frontend redirects browser to redirectAcsUrl 5. Cardholder completes bank authentication 6. Bank redirects to successUrl (approved) or failUrl (rejected) 7. Your server-side handler receives a POST with payment data 8. Backend confirms payment status via GET /payments/{id} ``` ### Step 4 — Redirect to Challenge ```javascript const response = await createPayment(paymentData); if (response.data.status === 'CHALLENGE' && response.data.redirectAcsUrl) { // Full-page redirect to bank's 3DS page window.location.href = response.data.redirectAcsUrl; } ``` ### Step 7a — Success URL Handler The 3DS provider sends a POST to your `successUrl` with payment data: ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": "99.99", "approved": "true" } ``` **Server-side handler example (Node.js / Express):** ```javascript // POST /3ds/success app.post('/3ds/success', async (req, res) => { const { paymentId, externalPaymentId } = req.body; // CRITICAL: Always verify via API — don't trust the redirect payload alone const payment = await fetch( `https://sandbox-gw.simpleps.com/payments/${externalPaymentId}`, { headers: { 'Authorization': `Bearer ${accessToken}` } } ).then(r => r.json()); if (payment.status === 'AUTHORIZED' || payment.status === 'CAPTURED') { // Payment confirmed — show success page res.redirect(`/order/${externalPaymentId}/confirmed`); } else { // Unexpected state — show error res.redirect(`/order/${externalPaymentId}/error`); } }); ``` ### Step 7b — Failure URL Handler The 3DS provider sends a POST to your `failUrl`: ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalId": "order-12345", "amount": 99.99, "approved": false, "status": "Rejected by ACS", "responseMsg": "Rejected by ACS", "responseCode": "99", "signatureVerification": "N", "acsChallengeRequired": true, "acsParStatus": "N" } ``` ```javascript // POST /3ds/fail app.post('/3ds/fail', (req, res) => { // Authentication failed — redirect user back to payment page res.redirect('/checkout?error=3ds_failed'); }); ``` > **Note:** When authentication fails, the payment was never authorized. Do not attempt to capture or void. --- ## Option 2: JavaScript PostMessage For single-page apps and embedded checkout experiences, you can open the 3DS challenge in an **iframe** and receive the result via the browser's `postMessage` API — no full-page redirect needed. ### PostMessage URL Whitelisting (Required) The URL of the page that will receive the `postMessage` events must be registered with Inyo. This is a security measure to ensure that only your intended page can listen to 3DS authentication results. - **Before going live**, provide Inyo with the exact origin URL (e.g., `https://checkout.yoursite.com`) of the page that listens for `postMessage` events. - **Any change to the URL** must be communicated to Inyo so the whitelist can be updated. - 3DS results will not be delivered via `postMessage` to non-whitelisted origins. > This is in addition to the tokenizer domain whitelisting described in [Tokenizing Cards](../../../../tokenizing-cards.md). Both the tokenizer page URL and the PostMessage listener page URL must be registered. ### Tokenizer Configuration Set `enablePostMessage: true` in `threeDSData`: ```javascript const tokenizer = new InyoTokenizer({ targetId: '#payment-form', publicKey: 'YOUR_PUBLIC_KEY', threeDSData: { enable: true, enablePostMessage: true }, successCallback: handleSuccess, errorCallback: handleError }); ``` > **Key difference:** When using `enablePostMessage: true`, you do **not** need to provide `successUrl` or `failUrl`. The result is delivered via `postMessage` instead of a redirect. ### Flow ``` 1. Frontend tokenizes card → receives token 2. Backend calls POST /v2/payment with token 3. API returns status: "CHALLENGE" + redirectAcsUrl 4. Frontend opens redirectAcsUrl in an iframe (or modal) 5. Cardholder completes bank authentication inside the iframe 6. 3DS provider sends a postMessage to the parent window 7. Frontend listens for the message and handles the result 8. Backend confirms payment status via GET /payments/{id} ``` ### Step 4 — Open Challenge in Iframe ```html ``` ```javascript const response = await createPayment(paymentData); if (response.data.status === 'CHALLENGE' && response.data.redirectAcsUrl) { // Show the iframe container document.getElementById('threeds-container').style.display = 'block'; // Load the 3DS challenge page document.getElementById('threeds-iframe').src = response.data.redirectAcsUrl; } ``` ### Step 7 — Listen for PostMessage ```javascript window.addEventListener('message', async (event) => { // Validate the origin for security if (!event.origin.includes('simpleps.com')) return; const data = event.data; // Hide the iframe document.getElementById('threeds-container').style.display = 'none'; if (data.approved === true || data.approved === 'true') { // 3DS succeeded — verify payment status from your backend const paymentStatus = await verifyPaymentOnBackend(data.externalPaymentId); if (paymentStatus === 'AUTHORIZED' || paymentStatus === 'CAPTURED') { showSuccessMessage(); } else { showErrorMessage('Payment could not be confirmed.'); } } else { // 3DS failed — let user retry showErrorMessage('Card verification failed. Please try again or use a different card.'); } }); ``` ### PostMessage Payload — Success ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": 99.99, "approved": true } ``` ### PostMessage Payload — Failure ```json { "paymentId": "dce568c6-98ec-456c-bb33-4a6809c4fff8", "externalPaymentId": "order-12345", "amount": 99.99, "approved": false, "status": "Rejected by ACS", "responseCode": "99" } ``` ### Complete SPA Example ```javascript // Full 3DS handling for a single-page app class ThreeDSHandler { constructor() { this.iframe = document.getElementById('threeds-iframe'); this.container = document.getElementById('threeds-container'); this.pendingResolve = null; window.addEventListener('message', (event) => { if (!event.origin.includes('simpleps.com')) return; this.handleResult(event.data); }); } // Returns a promise that resolves when 3DS completes startChallenge(redirectAcsUrl) { return new Promise((resolve) => { this.pendingResolve = resolve; this.container.style.display = 'block'; this.iframe.src = redirectAcsUrl; }); } handleResult(data) { this.container.style.display = 'none'; this.iframe.src = ''; if (this.pendingResolve) { this.pendingResolve({ success: data.approved === true || data.approved === 'true', paymentId: data.paymentId, externalPaymentId: data.externalPaymentId }); this.pendingResolve = null; } } } // Usage in your checkout flow: const threeds = new ThreeDSHandler(); async function processPayment(paymentData) { const response = await createPayment(paymentData); if (response.data.status === 'CHALLENGE') { const result = await threeds.startChallenge(response.data.redirectAcsUrl); if (result.success) { // Verify on backend, then show confirmation const verified = await verifyPayment(result.externalPaymentId); showConfirmation(verified); } else { showRetryPrompt(); } } else if (response.data.status === 'AUTHORIZED') { showConfirmation(response.data); } else { showDeclineMessage(response.data.message); } } ``` --- ## Choosing Between Redirect and PostMessage | Consideration | URL Redirect | PostMessage | |---|---|---| | **User experience** | Full page navigation; user leaves your checkout | Seamless; challenge appears in iframe/modal | | **Implementation complexity** | Simpler — just set URLs | More code — manage iframe + event listener | | **Server requirements** | Need server-side POST handlers for success/fail URLs | No server handlers needed for 3DS result | | **SPA compatibility** | Requires workarounds (state loss on redirect) | Native fit for SPAs | | **Security** | Result delivered server-to-server (POST to your URL) | Result delivered client-side (validate origin!) | | **Mobile webview** | Works everywhere | Some webviews restrict iframe postMessage | **Recommendation:** - Use **URL Redirect** if you have a traditional server-rendered checkout or need maximum compatibility. - Use **PostMessage** if you're building an SPA or want an embedded checkout experience without page navigation. Regardless of which option you choose, **always verify the payment status via the API** (`GET /payments/{externalPaymentId}`) before fulfilling the order. --- ## Testing 3DS Use the 3DS-enabled test cards from the [Test Data](../../../../test-data/cards.md) page: | Card Number | Network | 3DS Type | |---|---|---| | `5413330033003303` | Mastercard | Challenge | | `5169527513596963` | Mastercard | Challenge | | `4983305199046950` | Visa | Challenge | | `5454545454545454` | Mastercard | Data-only | | `4975303994654672` | Visa | Data-only | | `6011926557021045` | Discover | Data-only | ## Implementation Checklist - [ ] Register your tokenizer page URL with Inyo (required for CORS) - [ ] Choose your integration method: URL Redirect or PostMessage - [ ] **If Redirect:** Configure `successUrl` and `failUrl` in tokenizer; implement server-side POST handlers - [ ] **If PostMessage:** Register your PostMessage listener page URL with Inyo; set `enablePostMessage: true` in tokenizer; implement `message` event listener with origin validation - [ ] Detect `status: "CHALLENGE"` in payment responses - [ ] Handle the 3DS result (success and failure) - [ ] **Always** verify payment status via GET API after 3DS completes - [ ] Test with 3DS test cards in sandbox - [ ] Handle edge cases: user closes iframe/tab, timeout, network errors - [ ] Notify Inyo of any URL changes before deploying to new domains --- description: >- Capture a pre-authorized payment to initiate settlement. Supports full and partial capture. --- # Capture Capturing a payment confirms that it's ready for financial settlement. This transfers the authorized funds from the cardholder's account to your merchant account. ## Key Rules - If not captured **within 7 days**, the authorization is **automatically voided** by the system - Settlement timing begins from the **moment of capture**, not authorization - A payment can only be captured **once** (but you can capture a partial amount) - If the payment was created with `"capture": true`, it was already captured — attempting again returns an error - If no amount is specified, the **full authorized amount** is captured ## Endpoint ``` POST https://sandbox-gw.simpleps.com/payments/{externalPaymentId}/capture ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Body (Optional) Omit the body to capture the full authorized amount. Include it for a partial capture: ```json { "amount": { "total": 50.00, "currency": "USD" } } ``` | Field | Type | Required | Description | |---|---|---|---| | `amount.total` | number | ❌ | Amount to capture (must be ≤ authorized amount) | | `amount.currency` | string | ❌ | Currency code (must match the authorization) | ## Example — Full Capture ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/capture \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' ``` ## Example — Partial Capture ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/capture \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "amount": { "total": 50.00, "currency": "USD" } }' ``` ## Response (200) ```json { "paymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "parentPaymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "externalPaymentId": "order-12345", "amount": 100.10, "created": "2024-04-22 16:22:06", "approved": true, "message": "Payment Approved", "automaticReversed": false, "status": "CAPTURED", "captured": true, "voided": false, "authCode": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "issuerName": "BANK OF AMERICA", "issuerCountry": "US", "cvcResult": "APPROVED", "avsResult": "NOT_SENT" } ``` For partial captures, the `status` will be `PARTIALLY_CAPTURED`. ## What's Next - **Captured?** → You can now [refund](refund.md) the payment (full or partial) - **Changed your mind?** → Only uncaptured payments can be [voided](void.md) --- description: >- Refund a captured payment, fully or partially. Multiple partial refunds are supported until the full amount is returned. --- # Refund Refunding reverses a previously captured payment and returns funds to the cardholder. The time for the refund to appear on the cardholder's statement varies by issuing bank. ## Key Rules - Only possible for **captured** payments (use [Void](void.md) for uncaptured authorizations) - You can refund the **full amount** or a **partial amount** - **Multiple partial refunds** are allowed until the full captured amount is refunded - Once fully refunded, further refund attempts return an error - If no amount is specified, the **full captured amount** is refunded ## Endpoint ``` POST https://sandbox-gw.simpleps.com/payments/{externalPaymentId}/refund ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Body (Optional) Omit the body to refund the full captured amount. Include it for a partial refund: ```json { "amount": { "total": 25.00, "currency": "USD" } } ``` | Field | Type | Required | Description | |---|---|---|---| | `amount.total` | number | ❌ | Amount to refund (must be ≤ remaining captured amount) | | `amount.currency` | string | ❌ | Currency code (must match the original payment) | ## Example — Full Refund ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/refund \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' ``` ## Example — Partial Refund ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/refund \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "amount": { "total": 25.00, "currency": "USD" } }' ``` ## Response (200) ```json { "paymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "parentPaymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "externalPaymentId": "order-12345", "amount": 100.10, "created": "2024-04-22 16:22:06", "approved": true, "message": "Payment Approved", "automaticReversed": false, "status": "REFUNDED", "captured": true, "voided": false, "authCode": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "issuerName": "BANK OF AMERICA", "issuerCountry": "US", "cvcResult": "APPROVED", "avsResult": "NOT_SENT" } ``` For partial refunds, the `status` will be `PARTIALLY_REFUNDED` until the full amount is returned. --- description: >- Void a pre-authorized payment to cancel it before settlement. The authorization is released back to the cardholder. --- # Void Voiding cancels an authorized payment **before capture**. The reserved funds are released back to the cardholder's account. ## Key Rules - Only possible if the payment has **not yet been captured** - Voids are **irreversible** — the payment cannot be re-captured after voiding - If the payment was already captured, use [Refund](refund.md) instead - The `externalPaymentId` identifies the payment to void ## Endpoint ``` POST https://sandbox-gw.simpleps.com/payments/{externalPaymentId}/void ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Body None required. ## Example ```bash curl -X POST https://sandbox-gw.simpleps.com/payments/order-12345/void \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' ``` ## Response (200) ```json { "paymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "parentPaymentId": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "externalPaymentId": "order-12345", "amount": 100.10, "created": "2024-04-22 16:22:06", "approved": true, "message": "Payment Approved", "automaticReversed": false, "status": "VOIDED", "captured": false, "voided": true, "authCode": "bfb9eacb-7c72-4cc8-9cae-afd9164ec792", "issuerName": "BANK OF AMERICA", "issuerCountry": "US", "cvcResult": "APPROVED", "avsResult": "NOT_SENT" } ``` > **Note:** The original void doc showed `"status": "CAPTURED"` in the response — this was incorrect. A voided payment has `"status": "VOIDED"`. --- description: >- Pull funds from a US bank account using ACH. No tokenization required — provide bank details directly in the payment request. --- # ACH (Bank Account) ACH payments pull funds directly from a US bank account using the account number and routing number. Unlike card payments, ACH does not require tokenization — bank details are included directly in the API request. > **Note:** ACH payments are always captured immediately. Pre-authorization is not supported for ACH. ## Example Request ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "ach-order-001", "ipAddress": "203.0.113.42", "paymentType": "PULL", "capture": true, "amount": { "total": 19.99, "currency": "USD" }, "sender": { "firstName": "John", "lastName": "Smith", "address": { "countryCode": "US", "stateCode": "NY", "city": "New York", "line1": "123 Main Street", "line2": "Apt 25B", "zipCode": "10001" }, "paymentMethod": { "type": "BANK_DEPOSIT", "accountNumber": "123456789", "routingNumber": "021000021", "accountType": "CHECKING" } } }' ``` ## Payment Method Fields | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"BANK_DEPOSIT"` | | `accountNumber` | string | ✅ | Bank account number (6–20 digits) | | `routingNumber` | string | ✅ | ABA routing number (9 digits) | | `accountType` | string | ✅ | Account type (see below) | ### Account Types | Value | Description | |---|---| | `CHECKING` | Personal checking account | | `SAVINGS` | Personal savings account | | `BUSINESS_CHECKING` | Business checking account | | `BUSINESS_SAVINGS` | Business savings account | ## Response ACH payments return the same response structure as card payments. Since `capture` is `true`, the status will be `CAPTURED` on success: ```json { "status": 200, "data": { "paymentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "parentPaymentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "externalPaymentId": "ach-order-001", "amount": 19.99, "created": "2025-03-31 10:15:30", "approved": true, "message": "Payment Approved", "status": "CAPTURED", "captured": true, "voided": false, "responseCode": "00" } } ``` ## Key Differences from Card Payments | Feature | Card | ACH | |---|---|---| | Tokenization required | ✅ Yes | ❌ No | | Pre-authorization | ✅ Supported | ❌ Not supported | | Capture/Void lifecycle | ✅ Full lifecycle | ❌ Auto-captured | | 3D Secure | ✅ Possible | ❌ Not applicable | | Refund | ✅ Supported | ✅ Supported | | Settlement speed | Same day – 2 days | 1–3 business days | --- description: >- Query and paginate through payment history — retrieve payments by external ID or fetch a filtered list of transactions. --- # Listing Payments The Inyo Gateway provides endpoints to query transaction details and history: - [**Get a single payment**](by-external-id.md) — Retrieve full details and history for a specific transaction - [**List all payments**](get-a-list-of-payments.md) — Search and paginate through your transactions --- description: >- Retrieve full details, history, and fee breakdown for a specific payment using its external ID. --- # Get Payment by External ID Query the full details of a payment including its lifecycle history, fees, and card information. ## Endpoint ``` GET https://sandbox-gw.simpleps.com/payments/{externalPaymentId} ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | ## Example Request ```bash curl -X GET https://sandbox-gw.simpleps.com/payments/order-12345 \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' ``` ## Response (200) ```json { "parentPaymentId": "9a8dd6f3-105c-40a9-be13-ed95b2b2274b", "externalId": "order-12345", "requestedOn": "2025-01-21 17:15:14", "capturedOn": "2025-01-18 14:59:53", "refundedOn": "2025-01-21 17:15:14", "amount": 57.00, "capturedAmount": 57.00, "refundedAmount": 57.00, "amountRequested": 57.00, "currency": "USD", "status": "REFUNDED", "approved": true, "billing": { "stateCode": "FL", "city": "Orlando", "line1": "12516 Britwell Ct", "state": "FL", "zipCode": "32837" }, "ipAddress": "203.0.113.42", "customer": { "firstName": "MIKE", "lastName": "JOSEPH", "phoneNumber": "+1231232123", "email": "mike.joseph@example.com" }, "transactionFees": [ { "sourceId": "9a8dd6f3-...", "source": "PAYMENT", "eventId": "PRE_AUTH", "dtCreated": "2025-01-18 14:59:48", "amount": 0.57 }, { "sourceId": "0e444b74-...", "source": "PAYMENT", "eventId": "CAPTURE", "dtCreated": "2025-01-18 14:59:53", "amount": 0.00 }, { "sourceId": "9989afba-...", "source": "PAYMENT", "eventId": "REFUND", "dtCreated": "2025-01-21 17:15:14", "amount": 0.00 } ], "history": [ { "paymentId": "9a8dd6f3-...", "requestedOn": "2025-01-18 14:59:47", "code": "PAYMENT", "status": "AUTHORIZED", "description": "Payment Approved", "requestedAmount": 57.00 }, { "paymentId": "0e444b74-...", "requestedOn": "2025-01-18 14:59:53", "code": "CAPTURE", "status": "CAPTURED", "description": "Payment Approved", "requestedAmount": 57.00 }, { "paymentId": "9989afba-...", "requestedOn": "2025-01-21 17:15:14", "code": "REFUND", "status": "REFUNDED", "description": "Payment Approved", "requestedAmount": 57.00 } ], "card": { "lastFourDigits": "2929", "bin": "479213", "schemeId": "VISA", "issuer": "TD BANK, NATIONAL ASSOCIATION", "country": "UNITED STATES", "countryCode": "US", "cardType": "DEBIT", "cardCategory": "CLASSIC", "currencyCode": "USD" } } ``` ## Response Fields ### Root | Field | Type | Description | |---|---|---| | `parentPaymentId` | string | Root payment ID | | `externalId` | string | Your external payment ID | | `requestedOn` | string | Timestamp of the most recent operation | | `capturedOn` | string | Capture timestamp (null if not captured) | | `refundedOn` | string | Refund timestamp (null if not refunded) | | `amount` | number | Original authorized amount | | `capturedAmount` | number | Amount that was captured | | `refundedAmount` | number | Amount that was refunded | | `currency` | string | ISO 4217 currency code | | `status` | string | Current payment status | | `approved` | boolean | Whether the payment was approved | ### `transactionFees[]` | Field | Description | |---|---| | `eventId` | Fee event: `PRE_AUTH`, `CAPTURE`, `REFUND` | | `amount` | Fee amount for this event | | `dtCreated` | When the fee was incurred | ### `history[]` | Field | Description | |---|---| | `paymentId` | ID of the specific operation | | `code` | Operation type: `PAYMENT`, `CAPTURE`, `REFUND` | | `status` | Status after this operation | | `requestedAmount` | Amount for this operation | ### `card` | Field | Description | |---|---| | `lastFourDigits` | Last 4 digits of the card | | `bin` | Bank Identification Number (first 6 digits) | | `schemeId` | Card network: `VISA`, `MASTERCARD`, etc. | | `issuer` | Issuing bank name | | `cardType` | `DEBIT` or `CREDIT` | | `cardCategory` | Card tier (e.g., `CLASSIC`, `GOLD`, `PLATINUM`) | --- description: >- Retrieve a paginated list of all payments. Useful for reconciliation, reporting, and transaction management. --- # List Payments Retrieve all transactions in a paginated format. ## Endpoint ``` GET https://sandbox-gw.simpleps.com/payments?resultsPerPage={size}&page={number} ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | ### Query Parameters | Parameter | Type | Default | Description | |---|---|---|---| | `resultsPerPage` | number | 10 | Number of results per page | | `page` | number | 0 | Page number (zero-indexed) | ## Example Request ```bash curl -X GET 'https://sandbox-gw.simpleps.com/payments?resultsPerPage=10&page=0' \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' ``` ## Response (200) Returns an array of payment objects. Each object has the same structure as the [single payment response](by-external-id.md), including `history`, `transactionFees`, `card`, `billing`, and `customer` fields. ```json [ { "parentPaymentId": "9a8dd6f3-...", "externalId": "order-12345", "requestedOn": "2025-01-21 17:15:14", "amount": 57.00, "currency": "USD", "status": "REFUNDED", "approved": true, "history": [ ... ], "transactionFees": [ ... ], "card": { ... } }, { "parentPaymentId": "b2c3d4e5-...", "externalId": "order-12346", "requestedOn": "2025-01-22 09:30:00", "amount": 150.00, "currency": "USD", "status": "CAPTURED", "approved": true, "history": [ ... ], "card": { ... } } ] ``` ## Pagination Use `page` and `resultsPerPage` to iterate through results: ```bash # First page GET /payments?resultsPerPage=25&page=0 # Second page GET /payments?resultsPerPage=25&page=1 ``` Increment `page` until the response returns fewer results than `resultsPerPage`, indicating you've reached the last page. --- description: >- Push transactions send funds from your account to a recipient's card, bank account, PIX key, or wallet — domestically or internationally. --- # Push Transaction Push (payout) transactions transfer funds to a recipient. The Inyo Gateway supports multiple destination methods: - **Cards** — Visa Direct / Mastercard Send (domestic and cross-border) - **ACH** — US domestic bank transfers - **PIX** — Brazilian instant payments - **Wallets** — Digital wallet payouts - **Bank Accounts** — Cross-border account payouts ## Endpoint ``` POST https://sandbox-gw.simpleps.com/v2/payment ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Structure ### Root Object | Field | Type | Required | Description | |---|---|---|---| | `externalPaymentId` | string | ✅ | Your unique payment identifier | | `ipAddress` | string | ✅ | Originator's IPv4 or IPv6 address | | `paymentType` | string | ✅ | `"PUSH"` | | `amount` | object | ✅ | Source amount (what you're sending) | | `recipientAmount` | object | ✅ | Destination amount (what recipient receives) | | `exchangeRate` | number | ✅ | FX rate applied between currencies | | `sender` | object | ✅ | Sender details | | `recipient` | object | ✅ | Recipient details and destination | ### `amount` Object (Source) | Field | Type | Required | Description | |---|---|---|---| | `total` | number | ✅ | Amount to send (must be ≥ 1) | | `currency` | string | ✅ | Source currency (e.g., `"USD"`) | ### `recipientAmount` Object (Destination) | Field | Type | Required | Description | |---|---|---|---| | `total` | number | ✅ | Amount recipient receives | | `currency` | string | ✅ | Destination currency (ISO 4217, e.g., `"BRL"`) | ### `sender` Object | Field | Type | Required | Description | |---|---|---|---| | `customer` | object | ✅ | Sender's personal information | | `customerAddress` | object | ✅ | Sender's address | #### `sender.customer` | Field | Type | Required | Description | |---|---|---|---| | `firstName` | string | ✅ | Sender's first name | | `lastName` | string | ✅ | Sender's last name | | `phoneNumber` | string | ✅ | Phone (7–15 digits) | | `documentNumber` | string | ✅ | ID document number (5–20 digits) | | `documentType` | string | ✅ | `NATIONAL_ID`, `PASSPORT`, or `DRIVER_LICENSE` | | `email` | string | ✅ | Email address | | `countryCodeAlpha3` | string | ✅ | ISO Alpha-3 country code (e.g., `"USA"`) | #### `sender.customerAddress` | Field | Type | Required | Description | |---|---|---|---| | `stateCode` | string | ✅ | State abbreviation (e.g., `"CA"`) | | `city` | string | ✅ | City name | | `line1` | string | ✅ | Street address line 1 | | `line2` | string | ❌ | Street address line 2 | | `zipCode` | string | ✅ | Postal/ZIP code | | `countryCode` | string | ❌ | ISO Alpha-2 country code | ### `recipient` Object | Field | Type | Required | Description | |---|---|---|---| | `customer` | object | ✅ | Recipient's personal information | | `customerAddress` | object | ✅ | Recipient's address | | `destination` | object | ✅ | Payout method and details | #### `recipient.customer` Same fields as `sender.customer`. #### `recipient.customerAddress` Same fields as `sender.customerAddress`. ### `recipient.destination` — PIX | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"PIX"` | | `pix.keyType` | string | ✅ | `"EMAIL"`, `"PHONE"`, `"DOCUMENT"` (CPF/CNPJ), or `"EVP"` (random key) | | `pix.key` | string | ✅ | The PIX key value | ### `recipient.destination` — Wallet | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"WALLET"` | | `wallet.walletId` | string | ✅ | Wallet identifier (email or ID) | ## Example — Push to PIX ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "push-001", "ipAddress": "203.0.113.42", "paymentType": "PUSH", "amount": { "total": 55.00, "currency": "USD" }, "recipientAmount": { "total": 273.63, "currency": "BRL" }, "exchangeRate": 4.975, "sender": { "customer": { "firstName": "John", "lastName": "Smith", "phoneNumber": "5551234567", "documentNumber": "050482156", "documentType": "NATIONAL_ID", "email": "john.smith@example.com", "countryCodeAlpha3": "USA" }, "customerAddress": { "stateCode": "CA", "city": "Los Angeles", "line1": "4429 Candlewood St", "zipCode": "90712", "countryCode": "US" } }, "recipient": { "customer": { "firstName": "Carlos", "lastName": "Silva", "phoneNumber": "1122334455", "documentNumber": "12345678900", "documentType": "PASSPORT", "email": "carlos.silva@example.com", "countryCodeAlpha3": "BRA" }, "customerAddress": { "stateCode": "RJ", "city": "Rio de Janeiro", "line1": "Rua das Laranjeiras 321", "state": "Rio de Janeiro", "zipCode": "22240-005" }, "destination": { "type": "PIX", "pix": { "keyType": "DOCUMENT", "key": "01034861788" } } } }' ``` ## Example — Push to Wallet ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "push-002", "ipAddress": "203.0.113.42", "paymentType": "PUSH", "amount": { "total": 100.00, "currency": "USD" }, "recipientAmount": { "total": 497.50, "currency": "BRL" }, "exchangeRate": 4.975, "sender": { "customer": { "firstName": "Jane", "lastName": "Doe", "phoneNumber": "5559876543", "documentNumber": "123456789", "documentType": "NATIONAL_ID", "email": "jane.doe@example.com", "countryCodeAlpha3": "USA" }, "customerAddress": { "stateCode": "NY", "city": "New York", "line1": "456 Park Avenue", "zipCode": "10022", "countryCode": "US" } }, "recipient": { "customer": { "firstName": "Ana", "lastName": "Souza", "phoneNumber": "21987654321", "documentNumber": "10987654321", "documentType": "PASSPORT", "email": "ana.souza@example.com", "countryCodeAlpha3": "BRA" }, "customerAddress": { "stateCode": "RJ", "city": "Rio de Janeiro", "line1": "Rua da Alfândega 45", "state": "Rio de Janeiro", "zipCode": "20010-030" }, "destination": { "type": "WALLET", "wallet": { "walletId": "ana.souza@wallet.com" } } } }' ``` ## Foreign Exchange For cross-currency push payments, you can fetch real-time FX rates before submitting: ```bash curl -X POST https://sandbox-gw.simpleps.com/foreign-exchange \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "sourceCurrencyCode": "USD", "destinationCurrencyCode": "BRL" }' ``` **Response:** ```json { "fxId": "a1b2c3d4-...", "conversionRate": 4.975, "quoteIdExpiryDateTime": "2025-03-31T15:30:00Z" } ``` Use the `conversionRate` as the `exchangeRate` in your push payment, and optionally pass `fxId` to lock in the quoted rate. > **Note:** FX quotes have a limited validity period. Check `quoteIdExpiryDateTime` and refresh if expired. --- description: >- Combine a pull (collect) and push (payout) in a single API call for streamlined fund collection and disbursement. --- # Pull and Push in One Step The `PULLPUSH` payment type combines fund collection and disbursement into a single API call. Funds are pulled from the sender's card or bank account and simultaneously pushed to the recipient's destination. This reduces latency and simplifies workflows where funds need to move between two parties in a single transaction. ## Endpoint ``` POST https://sandbox-gw.simpleps.com/v2/payment ``` **Headers:** | Header | Value | |---|---| | `Authorization` | `Bearer {accessToken}` | | `Content-Type` | `application/json` | ## Request Structure ### Root Object | Field | Type | Required | Description | |---|---|---|---| | `externalPaymentId` | string | ✅ | Your unique payment identifier | | `ipAddress` | string | ✅ | Originator's IPv4 or IPv6 address | | `paymentType` | string | ✅ | `"PULLPUSH"` | | `amount` | object | ✅ | Pull amount (sender side, in USD) | | `recipientAmount` | object | ✅ | Push amount (recipient side, any currency) | | `exchangeRate` | number | ✅ | FX rate between source and destination currencies | | `sender` | object | ✅ | Sender details and payment source | | `recipient` | object | ✅ | Recipient details and payout destination | ### Sender The sender structure is identical to a [Pull payment](pulling-funds/) — includes `customer`, `customerAddress`, and `source`: #### `sender.source` — Card | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"CARD"` | | `card.token` | string | ✅ | Card token UUID from tokenizer | #### `sender.source` — Bank Account | Field | Type | Required | Description | |---|---|---|---| | `type` | string | ✅ | `"BANK_ACCOUNT"` | | `accountType` | string | ✅ | `"savings"`, `"checking"`, etc. | | `accountNumber` | string | ✅ | Bank account number | | `routingNumber` | string | ✅ | ABA routing number | | `accountHolder` | object | ✅ | `{ type, firstName, lastName }` | ### Recipient The recipient structure is identical to a [Push payment](push-transaction.md) — includes `customer`, `customerAddress`, and `destination`. ## Example — PULLPUSH (Card → Wallet) ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "pullpush-001", "ipAddress": "203.0.113.42", "paymentType": "PULLPUSH", "amount": { "total": 100.00, "currency": "USD" }, "recipientAmount": { "total": 497.50, "currency": "BRL" }, "exchangeRate": 4.975, "sender": { "customer": { "firstName": "Lucas", "lastName": "Silva", "phoneNumber": "11999999999", "documentNumber": "12345678900", "documentType": "NATIONAL_ID", "email": "lucas.silva@example.com", "countryCodeAlpha3": "USA" }, "customerAddress": { "stateCode": "CA", "city": "San Francisco", "line1": "123 Mission St", "line2": "Suite 900", "state": "California", "zipCode": "94105" }, "source": { "type": "CARD", "card": { "token": "ab5fc589-8b48-4531-94c0-68b0629c13fe" } } }, "recipient": { "customer": { "firstName": "Ana", "lastName": "Souza", "phoneNumber": "21987654321", "documentNumber": "10987654321", "documentType": "PASSPORT", "email": "ana.souza@example.com", "countryCodeAlpha3": "BRA" }, "customerAddress": { "stateCode": "RJ", "city": "Rio de Janeiro", "line1": "Rua da Alfândega 45", "state": "Rio de Janeiro", "zipCode": "20010-030" }, "destination": { "type": "WALLET", "wallet": { "walletId": "ana.souza@wallet.com" } } } }' ``` ## Example — PULLPUSH (Bank Account → PIX) ```bash curl -X POST https://sandbox-gw.simpleps.com/v2/payment \ -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \ -H 'Content-Type: application/json' \ -d '{ "externalPaymentId": "pullpush-002", "ipAddress": "203.0.113.42", "paymentType": "PULLPUSH", "amount": { "total": 200.00, "currency": "USD" }, "recipientAmount": { "total": 995.00, "currency": "BRL" }, "exchangeRate": 4.975, "sender": { "customer": { "firstName": "Maria", "lastName": "Johnson", "phoneNumber": "5551234567", "documentNumber": "987654321", "documentType": "DRIVER_LICENSE", "email": "maria.johnson@example.com", "countryCodeAlpha3": "USA" }, "customerAddress": { "stateCode": "TX", "city": "Austin", "line1": "789 Congress Ave", "state": "Texas", "zipCode": "78701" }, "source": { "type": "BANK_ACCOUNT", "accountType": "checking", "accountNumber": "123456789", "routingNumber": "021000021", "accountHolder": { "type": "personal", "firstName": "Maria", "lastName": "Johnson" } } }, "recipient": { "customer": { "firstName": "Pedro", "lastName": "Santos", "phoneNumber": "11987654321", "documentNumber": "98765432100", "documentType": "NATIONAL_ID", "email": "pedro.santos@example.com", "countryCodeAlpha3": "BRA" }, "customerAddress": { "stateCode": "SP", "city": "São Paulo", "line1": "Av Paulista 1000", "state": "São Paulo", "zipCode": "01311-100" }, "destination": { "type": "PIX", "pix": { "keyType": "EMAIL", "key": "pedro.santos@example.com" } } } }' ``` ## Key Differences from Separate Pull + Push | Aspect | Separate calls | PULLPUSH | |---|---|---| | API calls | 2 (one pull, one push) | 1 | | Atomicity | Independent — push can fail after pull succeeds | Single transaction | | FX rate | Must manage independently | Specified in same request | | Use case | Complex flows with intermediate logic | Straight-through remittance | --- description: >- Receive real-time notifications when payment statuses change. Configure webhooks to automate your payment workflow. --- # Webhooks Webhooks deliver real-time notifications to your server when a payment reaches a specific status. Instead of polling the API for updates, your system receives a POST request with the payment details. ## Supported Events | Event | Trigger | Typical Action | |---|---|---| | `AUTHORIZED` | Payment successfully authorized (funds reserved) | Confirm payment method is valid | | `CAPTURED` | Payment captured and funds settled | Fulfill order, release goods | | `CHALLENGE` | 3DS challenge initiated; awaiting cardholder verification | Wait for follow-up (`AUTHORIZED` or `DECLINED`) | | `DECLINED` | Payment rejected by issuer or fraud rules | Prompt user for another method | | `VOIDED` | Authorization cancelled before capture | Update order as cancelled | | `REFUNDED` | Funds returned to cardholder (full or partial) | Confirm refund in your system | ## Webhook Configuration Webhook endpoints are configured during merchant onboarding. Contact the Inyo team to: 1. Provide your webhook URL(s) 2. Select which events you want to receive 3. Configure authentication (Basic auth or Bearer token) > **Webhook registration API**: Webhooks can also be managed programmatically via the notification endpoints. See the [OpenAPI spec](https://sandbox-gw.simpleps.com/q/swagger-ui/#/) for `POST`, `GET`, and `DELETE /notification` endpoints. ## Webhook Payload All webhook events deliver the same JSON structure — the full payment object at the time of the event: ```json { "paymentId": "469670e2-31e3-4700-b9f2-abee99418dc4", "parentPaymentId": "469670e2-31e3-4700-b9f2-abee99418dc4", "externalPaymentId": "order-12345", "redirectAcsUrl": "", "amount": 95.00, "created": "2025-01-21 16:31:02", "approved": true, "message": "Payment Approved", "acquirerMessage": "IN_PROCESS_AUTHORISED", "automaticReversed": false, "status": "AUTHORIZED", "captured": false, "voided": false, "authCode": "469670e2-31e3-4700-b9f2-abee99418dc4", "issuerName": "BANK OF AMERICA", "issuerCountry": "UNITED STATES", "cvcResult": "NOT_SENT", "avsResult": "NOT_SENT", "aavAddressResult": "NOT_CHECKED", "aavPostcodeResult": "NOT_CHECKED", "aavCardholderNameResult": "N/A", "aavTelephoneResult": "NOT_CHECKED" } ``` ## Payload Fields See [Authorizing a Card Payment](payment/pulling-funds/cards/authorizing/) for a full description of all response fields. The webhook payload is identical to the payment response object. ## Handling Webhooks ### Best Practices 1. **Return 200 quickly** — Acknowledge receipt before doing heavy processing. Queue the event for async handling. 2. **Verify the source** — Validate that webhook requests come from Inyo's IP range or verify the authentication credentials. 3. **Handle duplicates** — Webhooks may be delivered more than once. Use `paymentId` + `status` as an idempotency key. 4. **Check `status`, not just `approved`** — The `status` field (`AUTHORIZED`, `CAPTURED`, `DECLINED`, etc.) is the authoritative state of the payment. 5. **Don't rely solely on webhooks** — Always confirm the payment status via the [GET payment endpoint](payment/pulling-funds/listing-payments/by-external-id.md) before fulfilling orders. Webhooks are notifications, not the source of truth. ### Example Handler (Node.js) ```javascript app.post('/webhooks/inyo', (req, res) => { // Acknowledge immediately res.status(200).send('OK'); const payment = req.body; const { externalPaymentId, status, paymentId } = payment; switch (status) { case 'AUTHORIZED': // Payment authorized — ready to capture markOrderAsAuthorized(externalPaymentId, paymentId); break; case 'CAPTURED': // Funds settled — fulfill the order fulfillOrder(externalPaymentId); break; case 'DECLINED': // Payment failed — notify customer notifyDecline(externalPaymentId, payment.message); break; case 'VOIDED': // Authorization cancelled cancelOrder(externalPaymentId); break; case 'REFUNDED': // Funds returned processRefund(externalPaymentId, payment.amount); break; } }); ``` ## Retry Policy If your endpoint returns a non-2xx response or times out, Inyo will retry delivery with exponential backoff. Ensure your handler is idempotent to handle retries safely. --- description: >- Domain tables reference index for the Payment Gateway API — payment status lifecycle states and internal response code mappings. --- # Domain Tables Reference tables for payment processing: - [**Payment Status**](payment-status.md) — All possible payment lifecycle states - [**Response Codes**](response-code.md) — Gateway and issuer response codes with descriptions --- description: >- The Inyo Gateway uses a comprehensive set of response codes to indicate the status and results of transaction processing --- # Response Code Each response code is associated with a specific context and HTTP status code, providing clarity and actionable insights.. ## Customer-Facing Messages — Security Guidelines > **⚠️ Important:** Internal response codes must **never** be exposed directly to end users. Detailed error codes reveal information about your payment infrastructure, fraud rules, and issuer behavior that can be exploited by bad actors to refine fraudulent transactions. Instead, map internal codes to **generic, user-friendly messages**. The table below provides a recommended mapping: ### Recommended Customer-Facing Messages #### "Check if the card information provided is correct." Use for tokenization errors, invalid card data, CVV/expiry mismatches, and address verification failures. | Codes | | --------------------------------------------------------------------------------------------------------------------------------- | | `CAD_001` `CAD_002` `CAD_003` `CAD_004` `CAD_005` `CARD_010` `CA_006` `CA_007` | | `PAY_035` `PAY_041` `PAY_046` `PAY_045` `PAY_029` `PAY_087` | | `PAY_050` `PAY_049` `PAY_048` `PAY_047` `PAY_052` `PAY_051` `PAY_054` `PAY_053` `PAY_056` `PAY_055` `PAY_058` `PAY_057` `PAY_059` | #### "Additional verification is required. Please try again and complete verification." Use for PIN-related errors, 3DS failures, and additional authentication requests. | Codes | | ----------------------------------------------------------- | | `PAY_088` `PAY_097` `PAY_099` `PAY_107` `PAY_113` `PAY_114` | | `PAY_122` `PAY_123` `PAY_124` `PAY_125` `PAY_146` | #### "Your bank declined this payment. Please contact your bank or try another payment method." Use for issuer declines, card restrictions, expired cards, and network-level rejections. This is the catch-all for most issuer-driven failures. | Codes | | ------------------------------------------------------------------------------- | | `PAY_067` `PAY_063` `PAY_064` `PAY_680` `PAY_700` `PAY_699` `PAY_042` | | `PAY_090` `PAY_089` `PAY_072` `PAY_073` `PAY_074` `PAY_075` | | `PAY_079` `PAY_078` `PAY_080` `PAY_081` `PAY_082` `PAY_083` `PAY_085` `PAY_086` | | `PAY_091` `PAY_092` `PAY_093` `PAY_094` `PAY_096` | | `PAY_100` `PAY_101` `PAY_102` `PAY_103` `PAY_104` `PAY_105` `PAY_106` | | `PAY_108` `PAY_109` `PAY_110` `PAY_112` | | `PAY_116` `PAY_117` `PAY_118` `PAY_119` `PAY_120` `PAY_121` | | `PAY_126` `PAY_127` `PAY_128` `PAY_130` `PAY_131` `PAY_132` `PAY_133` | #### "This payment couldn't be processed. Try another payment method or contact support." Use for fraud/risk blocks, blacklist hits, and gateway-level rejections. Keep the message intentionally vague — do not hint at fraud detection. | Codes | | ----------------------------------------------------------- | | `PAY_030` `PAY_038` `PAY_039` `PAY_040` | | `PAY_139` `PAY_140` `PAY_141` `PAY_145` `PAY_066` `PAY_508` | #### "Insufficient funds. Please use another payment method or add funds and try again." Use for balance-related declines. | Codes | | ------------------------------------------------- | | `PAY_084` `PAY_499` `PAY_256` `PAY_231` `PAY_016` | #### "This payment exceeds the allowed limit. Please use a smaller amount or try another method." Use for amount limit violations. | Codes | | --------------------------------------------------------------------- | | `PAY_399` `PAY_630` `PAY_515` `PAY_481` `PAY_482` `PAY_444` `PAY_096` | #### "Transaction limit reached. Please try again later or use another method." Use for velocity/frequency limit violations. | Codes | | ------------------------------------------------------------------------------- | | `PAY_484` `PAY_483` `PAY_514` `PAY_513` `PAY_512` `PAY_511` `PAY_510` `PAY_509` | #### "This request has been processed. Please avoid resubmitting the same transaction." Use for duplicate transaction attempts. | Codes | | ----------------------------------------------------------- | | `CAD_011` `PAY_620` `PAY_319` `PAY_318` `PAY_317` `PAY_320` | | `PAY_311` `PAY_310` `PAY_323` `PAY_011` `PAY_009` | #### "Bank details could not be validated. Please confirm the recipient bank information and try again." Use for payout/push errors related to recipient bank data validation. | Codes | | --------------------------------------------------------------------- | | `PAY_650` `PAY_621` `PAY_575` `PAY_574` `PAY_566` `PAY_565` `PAY_560` | | `PAY_590` `PAY_589` `PAY_587` `PAY_586` `PAY_553` `PAY_551` | | `PAY_333` `PAY_325` `PAY_344` `PAY_343` `PAY_342` | | `PAY_349` `PAY_348` `PAY_347` `PAY_346` | #### "Information is missing or invalid. Please review and try again." Use for generic validation errors, missing fields, and malformed requests. | Codes | | --------------------------------------------------------------------------------------------------------------------------------- | | `GE_002` `PAY_065` `PAY_447` `PAY_486` `PAY_274` `PAY_278` `PAY_276` `PAY_275` `PAY_277` | | `PAY_666` `PAY_678` `PAY_519` `PAY_060` `PAY_269` `PAY_271` `PAY_270` `PAY_524` `PAY_532` | | `PAY_212` `PAY_213` `PAY_211` `PAY_210` `PAY_209` `PAY_207` `PAY_206` `PAY_205` `PAY_204` `PAY_203` `PAY_202` `PAY_201` `PAY_200` | | `PAY_214` `PAY_215` `PAY_216` `PAY_217` `PAY_218` `PAY_219` `PAY_220` `PAY_221` `PAY_222` `PAY_223` `PAY_224` `PAY_225` | | `PAY_253` `PAY_252` `PAY_251` `PAY_250` `PAY_249` `PAY_248` `PAY_247` `PAY_246` `PAY_245` | | `PAY_260` `PAY_259` `PAY_267` `PAY_266` `PAY_265` `PAY_264` `PAY_263` `PAY_262` | #### "Can't process this request right now. Please try again later." Use for system errors, timeouts, and temporary failures. | Codes | | ---------------------------------------------------------- | | `GE_001` `PAY_640` `PAY_501` `PAY_505` `PAY_506` `PAY_507` | | `PAY_112` `PAY_281` `PAY_304` `PAY_480` | ### Implementation Example ```javascript const CUSTOMER_MESSAGES = { 'card_data': 'Check if the card information provided is correct.', 'verification': 'Additional verification is required. Please try again and complete verification.', 'issuer_decline': 'Your bank declined this payment. Please contact your bank or try another payment method.', 'fraud_block': "This payment couldn't be processed. Try another payment method or contact support.", 'insufficient': 'Insufficient funds. Please use another payment method or add funds and try again.', 'limit': 'This payment exceeds the allowed limit. Please use a smaller amount or try another method.', 'velocity': 'Transaction limit reached. Please try again later or use another method.', 'duplicate': 'This request has been processed. Please avoid resubmitting the same transaction.', 'bank_validation': 'Bank details could not be validated. Please confirm the recipient bank information and try again.', 'invalid_input': 'Information is missing or invalid. Please review and try again.', 'system_error': "Can't process this request right now. Please try again later.", }; // Map response codes to message categories function getCustomerMessage(responseCode) { // Your mapping logic here — see tables above // NEVER return the raw responseCode or internal message to the customer } ``` > **Tip:** Log the full `responseCode` and `message` internally for debugging and support, but only show the mapped customer-friendly message in your UI. *** ## Internal Response Codes Reference The table below is the **complete internal reference** for development and debugging. These codes and descriptions should only appear in server logs, admin dashboards, and support tools — never in customer-facing interfaces. #### Response Codes Table | Code | Description | | :--- | :--- | | 00 | Payment Approved | | GE_001 | Generic Error | | CAD_011 | External ID already exists | | PAY_700 | Blocked by cardholder/contact cardholder | | PAY_699 | Transaction not supported/blocked by issuer | | PAY_680 | Declined by issuer | | PAY_679 | Bank not found | | PAY_678 | Value provided for one of the fields is too short. | | PAY_677 | TransactionDetail secondaryId must be null. | | PAY_676 | SenderDetail paymentCredentialReference must be null. | | PAY_675 | Recipient card paymentCredentialReference must be null. | | PAY_674 | TaxAmount field in Structured Remittance Information is not supported by this route | | PAY_673 | Unstructured Remittance Information should not be present with Structured Remittance Information in payout | | PAY_672 | Structured remittance information is not supported for payment method 'W' | | PAY_671 | Tax amount is always positive and greater than zero | | PAY_670 | ReferredDocumentInformation data block exceeds the maximum length | | PAY_669 | Tax Amount and Tax Currency Code must be provided together | | PAY_668 | Structural Remittance Information is disabled for this environment | | PAY_667 | Structured Remittance array is larger than supported | | PAY_666 | Value provided for one of the fields has incorrect format | | PAY_665 | Amount in SRI does not equal payout request amount | | PAY_664 | Structural Remittance Information currency code must match recipientDetail.bank.currencyCode for selected route | | PAY_663 | Minimum required structured remittance information is not provided. Amount, Currency code and creditor reference information is required and creditor reference code must be SCOR for the selected route | | PAY_662 | StructuredRemittance data block exceeds the maximum length. | | PAY_661 | StructuredRemittance array not supported for selected route | | PAY_660 | Negative signed amount provided for an invoice | | PAY_659 | Positive signed amount provided for a credit note | | PAY_658 | None of the available routes for this transaction support structured remittance information | | PAY_657 | If structuredRemittance object is included in the payload then a referredDocumentInformation or a creditorReference array must be included | | PAY_656 | If structuredRemittance object is included in the payload then amountCurrencyCode is mandatory | | PAY_655 | If structuredRemittance object is included in the payload then amount is mandatory | | PAY_654 | If creditorReference is provided then code is mandatory | | PAY_653 | If creditorReference is provided then number is mandatory | | PAY_652 | If referredDocumentInformation is provided then number is mandatory | | PAY_651 | If referredDocumentInformation is provided then code is mandatory | | PAY_650 | Recipient bank account cannot be validated. Bank or identity information is missing or incorrect. | | PAY_649 | Payout method does not match the recipient details provided | | PAY_648 | Recipient account identifier phone number not in correct format | | PAY_647 | Only Recipient wallet account identifier type PHONENUMBER supported for this wallet operator | | PAY_646 | IBAN is not enabled on SEPA Instant | | PAY_645 | Route is not allowed. | | PAY_644 | The initiatingPartyId is too long. | | PAY_643 | Transaction not in a cancellable state. | | PAY_642 | The length of the field is incorrect. | | PAY_641 | Additional Data Name under Transaction Details is not in the correct format. | | PAY_640 | Undefined Internal Error. Please contact your Visa representative for assistance. | | PAY_639 | Quote ID is invalid | | PAY_638 | The currency pair associated with the Quote ID does not match the currency pair in the Payout request. | | PAY_637 | Quote ID does not belong to the given Initiating Party ID | | PAY_636 | Quote ID not found | | PAY_635 | Quote ID has expired | | PAY_634 | Initiating Party ID is not correct | | PAY_633 | Recipient tax code (RUC) is required and has not been provided or is not correct | | PAY_632 | Data for Sender country is required to process your payout request | | PAY_631 | Transaction Amount or derived Destination Amount is 0 | | PAY_630 | Transaction amount exceeds the transaction limit | | PAY_629 | Sender type supported is Individual for this payout | | PAY_628 | Data for Sender address is required to process your payout request | | PAY_627 | Data for Sender date of birth is required to process your payout request | | PAY_626 | Recipient type supported is Individual for this payout | | PAY_625 | Configuration error. Onboarding incomplete. Please contact your Visa representative. | | PAY_624 | Sender reference number contains special characters. | | PAY_623 | Recipient additional data value contains special characters or exceeds max length allowed. | | PAY_622 | One or more of sender or reciepient identity data is missing or invalid | | PAY_621 | Recipient bank code or bank account number is required to process your payout request | | PAY_620 | This is a duplicate transaction | | PAY_619 | Purpose of payment is not correct | | PAY_618 | Configuration error. Please contact your Visa representative. | | PAY_617 | Data provided for one or more Sender fields is not required for this destination country | | PAY_616 | Sender company registration number is not correct | | PAY_615 | Data for one or more Sender fields are required to process your payout request | | PAY_614 | Data for one or more Sender fields are not correct or contains special characters | | PAY_613 | Duplicate data for one or more Sender identification list fields exists | | PAY_612 | Sender ID number is not correct | | PAY_611 | Only one form of identification is required for the Sender | | PAY_610 | Data for one or more Sender fields contains alpha and/or special characters. | | PAY_609 | Data for one or more Sender fields contains special characters | | PAY_608 | Data for one or more Sender identification list fields is not correct | | PAY_607 | Sender date of birth is not correct | | PAY_606 | Sender type must be 'I' for Individual or 'C' for Company | | PAY_605 | Data for one or more Sender fields is required to process your payout request | | PAY_604 | Sender identity type is mandatory for this route | | PAY_603 | Sender province is not correct/ Sender province or state is not correct | | PAY_602 | Sender email address is not correct | | PAY_601 | Sender ID type is not correct | | PAY_600 | Sender ID issue country is not correct | | PAY_599 | Data for one or more Sender country fields is not correct | | PAY_598 | Data provided for one or more Sender fields is not currently supported | | PAY_597 | Combination of Recipient bank account number and Recipient bank code type is not correct | | PAY_596 | Combination of Recipient bank code and Recipient bank branch code is not correct | | PAY_595 | Recipient bank code is not currently supported for this payout request | | PAY_594 | Combination of Recipient 'IBAN' bank account number and Recipient bank code is not correct | | PAY_593 | Recipient bank account number has failed the modulus check | | PAY_592 | Recipient bank country code does not support IBAN account numbers | | PAY_591 | Recipient bank country code requires IBAN account number to process this payout request | | PAY_590 | Recipient bank BIC is not correct | | PAY_589 | Recipient bank BIC contains special characters. | | PAY_588 | Data for one or more fields are not correct or contains special characters. | | PAY_587 | Recipient bank BIC is not correct | | PAY_586 | Recipient bank BIC is required to process your payout request | | PAY_585 | Remove the Recipient bank account number suffix. The suffix is included in the Recipient bank account number. | | PAY_584 | Recipient bank account number suffix is required to process your payout request | | PAY_583 | Recipient bank account type contains special characters. | | PAY_582 | Recipient bank account type is not correct. | | PAY_581 | Recipient bank account type is required to process your payout request | | PAY_580 | Recipient bank account number suffix contains special characters. | | PAY_579 | Recipient bank account number suffix contains alpha and/or special characters. | | PAY_578 | Recipient bank account number has failed the modulus check. | | PAY_577 | Recipient bank account number contains special characters. | | PAY_576 | Recipient bank account number contains alpha and/or special characters. | | PAY_575 | Recipient bank account number is required to process your payout request. | | PAY_574 | Recipient bank account number is not correct | | PAY_573 | Combination of Recipient bank BIC and Recipient bank code is not correct | | PAY_572 | One or more of the beneficiary bank information fields provided is incorrect | | PAY_571 | Recipient bank branch code is not correct | | PAY_570 | The bank branch code provided conflicts with the rest of the recipient bank information in the payout request. | | PAY_569 | Recipient bank branch code should not be provided for this destination country | | PAY_568 | Recipient bank branch code contains alpha and/or special characters. | | PAY_567 | Recipient bank branch code is required to process your payout request | | PAY_566 | Combination of Recipient bank code and Recipient bank account number is not correct | | PAY_565 | Recipient bank code is not correct | | PAY_564 | The bank code provided conflicts with the rest of the recipient bank information in the payout request. | | PAY_563 | Recipient bank code should not be provided for this destination country | | PAY_562 | Recipient bank code contains alpha and/or special characters. | | PAY_561 | Recipient bank code contains special characters. | | PAY_560 | Recipient bank code is required to process your payout request | | PAY_559 | Recipient bank name is not currently supported for this payout request | | PAY_558 | Recipient bank account name is not correct | | PAY_557 | Recipient bank name contains special characters. | | PAY_556 | Recipient bank name is required to process your payout request | | PAY_555 | Recipient bank account name contains special characters. | | PAY_554 | Recipient bank account name is required to process your payout request. | | PAY_553 | Unable to validate this payout request becuase one or more of recipient bank information provided is missing or invalid. | | PAY_552 | Combination of Recipient bank country code and Recipient bank account number for 'IBAN' bank account number type is not correct | | PAY_551 | Recipient details are not in the correct format. Contact your Visa representative for assistance. | | PAY_550 | Sender details are not in the correct format. Contact your Visa representative for assistance. | | PAY_549 | Destination country is not currently supported | | PAY_548 | Destination route not set up | | PAY_547 | Data provided for one or more Recipient fields is not required for this destination country | | PAY_546 | Duplicate data for one or more Recipient identification list fields exists | | PAY_545 | Only one form of identification is required for the Recipient | | PAY_544 | Data for one or more Recipient identification list fields is not correct | | PAY_543 | Recipient identity type is mandatory for this route | | PAY_542 | Recipient company registration number is not correct | | PAY_541 | Recipient email address is not correct | | PAY_540 | Recipient date of birth is not correct. | | PAY_539 | Recipient ID type is not correct | | PAY_538 | Data for one or more of Recipient country fields is not correct | | PAY_537 | Recipient ID number is not correct | | PAY_536 | Recipient ID issue country is not correct | | PAY_535 | Data provided for one or more Recipient fields is not currently supported | | PAY_534 | Data for one or more Recipient fields are not correct or contains special characters | | PAY_533 | Data for one or more Recipient fields contains alpha and/or special characters | | PAY_532 | The minor units in Transaction Amount does not align with currency exponent. | | PAY_531 | Data for one or more Recipient fields contains special characters | | PAY_530 | Data for one or more Recipient fields contains special characters | | PAY_529 | Recipient province or state is not correct | | PAY_528 | Data for one or more of Recipient fields are required to process your payout request | | PAY_527 | There is an issue with the data of this transaction and it cannot be processed | | PAY_526 | A balance for the currency and account requested does not exist. | | PAY_525 | No balances to return. | | PAY_524 | The currencyCode must be a alphabetic string of length 3. | | PAY_523 | TransactionDetail sourceAmount must be null | | PAY_522 | TransactionDetail sourceCurrencyCode must be null | | PAY_521 | The sourceAmount should not be supplied for Push to Account. | | PAY_520 | The sourceCurrencyCode should not be supplied for Push to Account. | | PAY_519 | Unknown query parameter | | PAY_518 | The number of calendar days requested must be an integer value between 1 and 30. | | PAY_517 | PayoutMethod must not be null | | PAY_516 | Subscriber not authorized to receive transaction | | PAY_515 | Transaction max amount exceeded | | PAY_514 | Monthly recipient velocity limit exceeded | | PAY_513 | Monthly sender velocity limit exceeded | | PAY_512 | Weekly recipient velocity limit exceeded | | PAY_511 | Weekly sender velocity limit exceeded | | PAY_510 | Daily recipient velocity limit exceeded | | PAY_509 | Daily sender velocity limit exceeded | | PAY_508 | Blacklist error | | PAY_507 | MFS system error | | PAY_506 | E-wallet system error | | PAY_505 | Transaction could not be executed | | PAY_504 | Subscriber not authorized to receive amount | | PAY_503 | Subscriber not found | | PAY_502 | Partner corridor not active | | PAY_501 | Transaction Error | | PAY_500 | Expired transfer proposal | | PAY_499 | Insufficient fund in merchant account | | PAY_498 | Sender middleName must be null | | PAY_497 | The idType must be one of CLIENT_REFERENCE_ID or PAYOUT_ID. | | PAY_496 | Payout not found | | PAY_495 | The route or wallet operator does not support the destination amount precision | | PAY_494 | A company cannot have a tax id | | PAY_493 | The additional identity data block contains an invalid item | | PAY_492 | The payoutSpeed is not correct. | | PAY_491 | Business Application ID is not correct | | PAY_490 | Configuration error. Please contact your Visa representative. | | PAY_489 | This payout request could not be processed. Please contact your Visa representative. | | PAY_488 | This payout request could not be processed. Please contact your Visa representative. | | PAY_487 | No route exists for this combination | | PAY_486 | The value provided for one or more request parameters is considered invalid. | | PAY_485 | Payment rejected for compliance related reason at partner. Contact your Visa representative for assistance. | | PAY_484 | Recipient’s transaction frequency exceeds the limit. | | PAY_483 | Sender’s transaction frequency exceeds the limit. | | PAY_482 | Transaction amount exceeds the transaction limit for the recipient. | | PAY_481 | Transaction amount exceeds the transaction limit for the sender | | PAY_480 | This request could not be processed. Please try again. | | PAY_479 | Recipient details validation in progress with partner | | PAY_478 | Sender minimum information is missing | | PAY_477 | A company cannot have a city of birth | | PAY_476 | A company cannot have a foreign id | | PAY_475 | A company cannot have a national id | | PAY_474 | A company cannot have a driving license | | PAY_473 | A company cannot have a passport | | PAY_472 | Recipient name or first name and last name are required. | | PAY_471 | A company cannot have a foreign id | | PAY_470 | A company cannot have a tax id | | PAY_469 | A company cannot have a national id | | PAY_468 | A company cannot have a driving license | | PAY_467 | A company cannot have a passport | | PAY_466 | A company cannot have date of birth or country of birth or city of birth | | PAY_465 | Recipient name or first name and last name are required. | | PAY_464 | Recipient name or first name and last name are required. | | PAY_463 | Recipient wallet currency is not supported for this wallet operator | | PAY_462 | Recipient wallet country is not supported for this wallet operator | | PAY_461 | Recipient identification id type is not supported | | PAY_460 | Recipient wallet account is unavailable | | PAY_459 | Recipient details mismatch at wallet operator. | | PAY_458 | Recipient wallet account not found | | PAY_457 | A company cannot have a country of birth | | PAY_456 | A company cannot have a date of birth | | PAY_455 | An Individual cannot have a legal registration number | | PAY_454 | Sender age is not supported | | PAY_453 | This combination of sender type and recipient type provided is not supported for this route | | PAY_452 | A company cannot have date of birth or country of birth or city of birth | | PAY_451 | Either senderAccountNumber or senderReferenceNumber is required | | PAY_450 | One of sender name or first name and last name must be provided | | PAY_449 | Sender name or first name and last name are required. | | PAY_448 | Country is not currently supported | | PAY_447 | Value provided for one of the fields is invalid | | PAY_446 | Sender-beneficiary relationship is not allowed | | PAY_445 | Value provided for one of the fields is too long | | PAY_444 | Payment value less than the minimum route limit | | PAY_443 | The sender identification idOwnerType must be null. | | PAY_442 | Sender middleName must be null | | PAY_441 | No eligible routes found which allow payer unstructured identity | | PAY_440 | The sender sourceOfFunds field must be null. | | PAY_439 | SenderReferenceNumber field must be null | | PAY_438 | Only sender name or firstName + lastName combination is allowed | | PAY_437 | Sender name or firstName + lastName combination is required | | PAY_436 | Only sender firstName + lastName combination or name is allowed | | PAY_435 | Sender firstName + lastName combination or name is required | | PAY_434 | Only sender name is allowed | | PAY_433 | Sender name is required | | PAY_432 | Only senderAccountNumber or senderReferenceNumber is allowed | | PAY_431 | SenderAccountNumber or senderReferenceNumber is required | | PAY_430 | Only sender firstName + lastName combination or fullName is allowed | | PAY_429 | Sender firstName + lastName combination or fullName is required | | PAY_428 | Only sender companyName or fullName is allowed | | PAY_427 | Sender companyName or fullName is required | | PAY_426 | The transaction currency should be same as either the client's settlement currency or recipient's wallet currency | | PAY_425 | The transaction currency should be same as either the client's settlement currency or recipient's bank currency | | PAY_424 | The minor units in structuredRemittance taxAmount does not align with currency exponent | | PAY_423 | The minor units in structuredRemittance Amount does not align with currency exponent | | PAY_422 | TransactionDetail structuredRemittance amount has invalid content | | PAY_421 | TransactionDetail structuredRemittance taxAmount is invalid | | PAY_420 | TransactionDetail structuredRemittance creditorReference number is too long | | PAY_419 | TransactionDetail structuredRemittance creditorReference number is too short | | PAY_418 | TransactionDetail structuredRemittance creditorReference number is missing | | PAY_417 | TransactionDetail structuredRemittance creditorReference code has invalid content | | PAY_416 | TransactionDetail structuredRemittance creditorReference code has invalid length | | PAY_415 | TransactionDetail structuredRemittance creditorReference code is missing | | PAY_414 | TransactionDetail structuredRemittance referredDocumentInformation relatedDate has invalid content | | PAY_413 | TransactionDetail structuredRemittance referredDocumentInformation number is too long | | PAY_412 | TransactionDetail structuredRemittance referredDocumentInformation number is too short | | PAY_411 | TransactionDetail structuredRemittance referredDocumentInformation number is missing | | PAY_410 | TransactionDetail structuredRemittance referredDocumentInformation code has invalid content | | PAY_409 | TransactionDetail structuredRemittance referredDocumentInformation code has invalid length | | PAY_408 | TransactionDetail structuredRemittance referredDocumentInformation code is missing | | PAY_407 | TransactionDetail structuredRemittance referredDocumentInformation is too long | | PAY_406 | TransactionDetail structuredRemittance taxCurrencyCode is Invalid | | PAY_405 | TransactionDetail structuredRemittance amountCurrencyCode is Invalid | | PAY_404 | TransactionDetail structuredRemittance amountCurrencyCode is missing | | PAY_403 | TransactionDetail structuredRemittance amount is missing | | PAY_402 | TransactionDetail structuredRemittance is too long | | PAY_401 | Transaction endToEndId must not be null | | PAY_400 | Transaction purpose of payment is required | | PAY_399 | Payment amount exceeds the route limit. | | PAY_398 | The transactionDetail transactionCurrencyCode must be a numeric of length 3. | | PAY_397 | The recipient middleName must be null. | | PAY_396 | The recipient identification idOwnerType must be null. | | PAY_395 | The recipientDetail address minorSubDivisionCode must be null. | | PAY_394 | The recipientDetail address streetName must be null. | | PAY_393 | The recipientDetail address streetName must be null. | | PAY_392 | Recipient wallet must be null. | | PAY_391 | The recipient bank must be null. | | PAY_390 | Recipient middleName must be null | | PAY_389 | Only recipient firstName + lastName combination or name is allowed | | PAY_388 | Recipient firstName + lastName combination or name is required | | PAY_387 | Only recipient name is allowed | | PAY_386 | Recipient name is mandatory | | PAY_385 | Only recipient name or firstName + lastName combination is allowed | | PAY_384 | Recipient name or firstName + lastName combination is required | | PAY_383 | No eligible routes found which allow payer unstructured identity | | PAY_382 | Recipient contact number is mandatory for this route. | | PAY_381 | The recipient card must be null. | | PAY_380 | Identity Type of beneficiary is required. | | PAY_379 | Conflicting recipient name, identity or type | | PAY_378 | The recipient payout method is not supported. | | PAY_377 | The recipient payoutMethod must not be null. | | PAY_376 | The recipient alias must be null. | | PAY_375 | Recipient identificationList must be provided | | PAY_374 | The recipient additional data key is not known. | | PAY_373 | At least one recipient additional data item must be supplied | | PAY_372 | Recipient bank BIC is not supported | | PAY_371 | Recipient bank BIC country does not match the SWIFT BIC country | | PAY_370 | Recipient bank BIC contains non-alphanumeric characters | | PAY_369 | Recipient bank BIC has not been supplied and is required in the territory | | PAY_368 | Recipient bank BIC supplied contradicts the bank identified by the supplied bank code | | PAY_367 | Recipient bank BIC does not exist in bank partner reference table | | PAY_366 | Recipient bank BIC country does not match bank account country | | PAY_365 | Recipient bank BIC code contradicts with bank code. | | PAY_364 | The recipient bank account number suffix is required in the territory. | | PAY_363 | Recipient bank account type is required in the territory | | PAY_362 | Recipient bank account IBAN supplied implies a country different than the country code supplied | | PAY_361 | Recipient bank Account Number and Sort Code mismatch | | PAY_360 | Recipient bank IBAN contradicts account number suffix | | PAY_359 | Recipient bank account IBAN contradicts the supplied account number | | PAY_358 | Recipient bank account IBAN contradicts the supplied routing number | | PAY_357 | Recipient bank account IBAN is contradicting | | PAY_356 | Recipient bank account IBAN failed modulus check | | PAY_355 | Recipient bank account IBAN country is not recognized | | PAY_354 | Recipient bank account IBAN is required in the territory | | PAY_353 | Recipient bank account number failed modulus check | | PAY_352 | recipient bank account number prefix contains non-numeric characters | | PAY_351 | recipient bank account number prefix is too short | | PAY_350 | recipient bank account number prefix is too long | | PAY_349 | Recipient bank branch code not found on CB.Net lookup | | PAY_348 | Recipient bank branch code is contradicting | | PAY_347 | Recipient bank branch code should not be supplied for this territory | | PAY_346 | Recipient bank branch code is required in the territory | | PAY_345 | ABA routing number and a Fedwire code have been supplied which contradict each other | | PAY_344 | Recipient bank code is not found on CB.Net lookup | | PAY_343 | Recipient bank ABA routing number contradicts the one derived from this account | | PAY_342 | The recipient bank ABA routing number is required. | | PAY_341 | Recipient bank sort code is not eligible for local schemes | | PAY_340 | Recipient bank sort code is required | | PAY_339 | The recipient bank sort code contains non-alphanumeric characters. | | PAY_338 | The recipient bank sort code contains non-numeric characters. | | PAY_337 | Recipient bank code contradicts the supplied account number | | PAY_336 | Recipient bank code is contradicting | | PAY_335 | Recipient bank code should not be supplied for this territory | | PAY_334 | Selected payment rail is not configured. Please contact your Visa representative. | | PAY_333 | The recipient bank information is not sufficient. | | PAY_332 | Only recipient firstName + lastName combination or fullName is allowed. | | PAY_331 | Recipient firstName + lastName combination or fullName is required. | | PAY_330 | Only recipient companyName or fullName is allowed. | | PAY_329 | Recipient companyName or fullName is mandatory. | | PAY_328 | Only recipient companyName or firstName + lastName combination or fullName is allowed. | | PAY_327 | Recipient companyName or firstName + lastName combination or fullName is required. | | PAY_326 | Recipient bank BIC is not found. | | PAY_325 | Insufficient bank data supplied. Please contact customer support for more info. | | PAY_324 | Transaction not in a cancellable state. | | PAY_323 | This transaction has already been cancelled. Current request is a duplicate. | | PAY_322 | Payout in queue. Wait for the status notification and retry cancellation later. | | PAY_321 | Payout attempt terminated unsuccessfully. | | PAY_320 | Prior payout exists with the same ClientReferenceID but with different key transaction data elements. | | PAY_319 | Current transaction is a duplicate. | | PAY_318 | This payout request is inconsistent with the previously processed payout transaction. | | PAY_317 | Current transaction is a duplicate. Original Payout attempt terminated unsuccessfully. | | PAY_316 | Current transaction is a duplicate. Original Payout attempt is in an undefined state. Please contact a Visa representative. | | PAY_315 | Current transaction is a duplicate. Original Payout attempt is in ERROR. Please contact a Visa representative. | | PAY_314 | Payout not cancellable at this time. Payout is in an inconsistent state. | | PAY_313 | The payout transaction is in an inconsistent state. Please contact your Visa Representative. | | PAY_312 | The payout transaction that has been queried is in an inconsistent/unknown state. | | PAY_311 | This payout request is inconsistent with the previously processed payout transaction. | | PAY_310 | This payout request matches with other payout transactions which are in an inconsistent state. Please contact your Visa Representative. | | PAY_309 | Transaction currency did not match the settlement currency or the destination currency. | | PAY_308 | Payout not cancellable. | | PAY_307 | Transaction currency is not same as Destination currency. | | PAY_306 | Validation failed for Retrieval Reference Number. This transaction can no longer be retried. | | PAY_305 | Final state of the transaction is unknown. | | PAY_304 | Transaction cannot be processed at this time, please contact Visa. | | PAY_303 | Payout is pending cancellation. | | PAY_302 | Payout is not cancellable. | | PAY_301 | Recipient bank account is inactive. | | PAY_300 | Payout request has an illegal character data. | | PAY_299 | Recipient bank account has duplicate data. | | PAY_298 | Additional data is required for this purpose of payment | | PAY_297 | Sender or recipient detail data is incomplete | | PAY_296 | Sender or recipient details exceeds max length. | | PAY_295 | Value contains non-supported characters. | | PAY_294 | Sender or recipient additional identity data contains more than one item of the same name | | PAY_293 | Recipient identification information is invalid or missing | | PAY_292 | Only one document number must be supplied for sender or recipient. | | PAY_291 | Sender or recipient identity number is not numeric. | | PAY_290 | Sender or recipient identity number is not binary. | | PAY_289 | Sender or recipient identity issue country is not allowed. | | PAY_288 | Sender or recipient country in address is not allowed. | | PAY_287 | Sender or recipient identification number is not numeric. | | PAY_286 | Sender or recipient identification type is required. | | PAY_285 | Wallet Operator does not support recipient verification functionality. | | PAY_284 | Route not supported, please contact Visa representative | | PAY_283 | The beneficiary Bank Account Intermediary Account supplied field contains not supported characters. | | PAY_282 | Recipient bank account is not supported. | | PAY_281 | Transaction cannot be processed at this time, please contact Visa. | | PAY_280 | The recipient country and currency route selected is not supported. | | PAY_279 | The recipient country and currency route selected is not supported. | | PAY_278 | Value provided for one of the fields is invalid. | | PAY_277 | Value provided for one of the fields has incorrect format. | | PAY_276 | Value provided for one of the fields is too short. | | PAY_275 | Value provided for one of the fields is too long. | | PAY_274 | Mandatory value is missing. | | PAY_273 | Mismatch between FX quote currency codes and transaction currency codes. | | PAY_272 | Fx is required for this transaction | | PAY_271 | Invalid or unsupported country code | | PAY_270 | Invalid person type provided | | PAY_269 | Address information is required and cannot be empty | | PAY_268 | User with username already exists | | PAY_267 | Agent ID cannot be null or blank | | PAY_266 | Password cannot be null or blank | | PAY_265 | Last name cannot be null or blank | | PAY_264 | First name cannot be null or blank | | PAY_263 | Email cannot be null or blank | | PAY_262 | Password cannot be null or blank | | PAY_261 | User with username does not exist | | PAY_260 | Username cannot be blank | | PAY_259 | Tenant cannot be blank | | PAY_258 | Bank not allowed to transaction | | PAY_257 | Invalid Bank Code | | PAY_256 | Insufficient balance | | PAY_255 | Card information is required | | PAY_254 | Bank account details are required | | PAY_253 | Account number is required | | PAY_252 | Routing number is required | | PAY_251 | Account holder information is required | | PAY_250 | Account type is required | | PAY_249 | A valid bank account type is required. Allowed values: savings, checking, loan, business_checking, or business_saving | | PAY_248 | A valid account holder type is required. Allowed values: personal or business | | PAY_247 | The "companyLegalName" field is required when the account holder is a business | | PAY_246 | Both "firstName" and "lastName" fields are required when the account holder is an personal | | PAY_245 | Invalid source type. Accepted values are "CARD" or "BANK_ACCOUNT". | | PAY_244 | The asset type is required for wallet creation. | | PAY_243 | The owner ID is required for wallet creation. | | PAY_242 | The beneficiary ID is required for wallet creation. | | PAY_241 | The wallet type is required for wallet creation. | | CAD_0207 | Payment Services is required | | CAD_0206 | Payment Methods is required | | CAD_0205 | Country is required | | CAD_0204 | Agent Trade Name is required | | CAD_0203 | Agent Commercial Name is required | | CAD_0202 | Agent Referer is required | | CAD_0201 | Agent id is required | | CAD_0200 | Phone is required | | CAD_0199 | Email is required | | PAY_231 | Insufficient balance to pull transaction | | CAD_015 | Agent Id already exists | | PAY_229 | No payment routes were found | | PAY_228 | Invalid Pix key. Please check and try again. | | PAY_227 | A Pix key is required. | | PAY_226 | A Pix account must be provided to receive the transfer. | | PAY_225 | The recipient country code (Alpha-3) is required for PUSH transactions. | | PAY_224 | The recipient object is required for a PUSH transaction. | | PAY_223 | The sender amount is required for PUSH transactions. | | PAY_222 | The exchange rate must be greater than zero. | | PAY_221 | Invalid currency code for the recipient country. Please check and try again. | | PAY_220 | Invalid currency code for the customer country. Please check and try again. | | PAY_219 | The recipient country code (Alpha-3) is required for PUSH transactions. | | PAY_218 | The customer country code (Alpha-3) is required for PULL transactions. | | PAY_217 | A destination account is required for PUSH transactions. | | PAY_216 | A source account is required for PULL transactions. | | PAY_215 | An amount is required for the transaction. | | PAY_214 | Invalid payment type. Accepted values are "PULL" or "PUSH". | | PAY_213 | Invalid page number. | | PAY_212 | Invalid page size. | | PAY_211 | The "to" date must be provided. | | PAY_210 | The "from" date must be provided. | | PAY_209 | The wallet ID must be in UUID v4 format. | | PAY_208 | The transfer amount must be greater than zero. | | PAY_207 | The destination wallet ID must be a valid UUID v4. | | PAY_206 | The source wallet ID must be a valid UUID v4. | | PAY_205 | An external ID is required. | | PAY_204 | Invalid transfer amount. | | PAY_203 | The destination wallet ID is required. | | PAY_202 | The source wallet ID is required. | | PAY_201 | The wallet ID is required. | | PAY_200 | A valid wallet payload is required. | | PAY_199 | The wallet type is required for wallet creation. | | PAY_198 | The beneficiary ID is required for wallet creation. | | PAY_197 | The owner ID is required for wallet creation. | | PAY_196 | The asset type is required for wallet creation. | | PAY_195 | Invalid source type. Accepted values are "CARD" or "BANK_ACCOUNT". | | PAY_194 | Both "firstName" and "lastName" fields are required when the account holder is an personal | | PAY_193 | The "companyLegalName" field is required when the account holder is a business | | PAY_192 | A valid account holder type is required. Allowed values: personal or business | | PAY_191 | A valid bank account type is required. Allowed values: savings, checking, loan, business_checking, or business_saving | | PAY_190 | Account type is required | | PAY_189 | Account holder information is required | | PAY_188 | Routing number is required | | PAY_187 | Account number is required | | PAY_186 | Bank account details are required | | PAY_185 | Card information is required | | 1 | Payment Pending | | PAY_183 | Invalid Phone Number | | PAY_182 | Invalid country code | | PAY_181 | Invalid Account Holder Type | | PAY_180 | Invalid Account Type | | PAY_155 | ACS ERROR - Data exchange response not found by paymentId | | PAY_154 | ACS ERROR - Data exchange not found by requestId | | PAY_153 | ACS ERROR - Data exchange not found by paymentId | | PAY_152 | ACS ERROR - Data exchange not found JWT by tenant and paymentId | | PAY_151 | ACS ERROR - Data exchange update invalid | | PAY_150 | ACS ERROR - Lookup response data not found find by paymentId | | PAY_149 | ACS ERROR - Lookup data not found find by paymentId and transactionId | | PAY_148 | ACS ERROR - Lookup data not found find by paymentId | | PAY_147 | ACS ERROR - Lookup data not found find by tenant and transactionId | | PAY_146 | Declined by 3DS Secure Code | | PAY_145 | Invalid cardholder - High Risk | | PAY_144 | Invalid cardholder - Email | | PAY_143 | Invalid cardholder name | | PAY_142 | Invalid Ip Address | | PAY_141 | Decline blacklist restrictions - Customer | | PAY_140 | Decline blacklist restrictions - IP | | PAY_139 | Decline blacklist restrictions - Card | | PAY_131 | Unable to go online; offline-declined | | PAY_130 | Revocation of all authorizations order | | PAY_128 | Transaction does not qualify for Visa PIN | | PAY_127 | Revocation of authorization order | | PAY_126 | Stop Payment Order | | PAY_125 | Card Authentication failed | | PAY_124 | Denied PIN change—requested PIN unsafe | | PAY_123 | Denied PIN unblock—PIN change or unblock request declined by issuer | | PAY_122 | Transaction amount exceeds preauthorized approval amount | | PAY_121 | Decline for CVV2 failure | | PAY_120 | Ineligible for resubmission | | PAY_119 | Cash request exceeds issuer or approved limit | | PAY_118 | Cash service not available | | PAY_117 | Force STIP | | PAY_116 | Surcharge amount not supported by debit network issuer. | | PAY_115 | Surcharge amount not permitted on Visa cards or EBT food stamps (U.S. acquirers only) | | PAY_114 | Verification data failed | | PAY_113 | Additional customer authentication required | | PAY_112 | System malfunction | | PAY_111 | Transaction cannot be completed - violation of law | | PAY_097 | PIN data required | | PAY_098 | Different value than that used for PIN encryption errors | | PAY_110 | Financial institution or intermediate network facility cannot be found for routing (receiving institution ID is invalid) | | PAY_109 | Issuer or switch inoperative and STIP not applicable or not available for this transaction; Timeout | | PAY_108 | Ineligible to receive financial position information (GIV) | | PAY_107 | Cannot verify PIN; for example, no PVV | | PAY_106 | No reason to decline a request for address verification, CVV2 verification, or a credit voucher or merchandise return | | PAY_105 | Negative CAM, dCVV, iCVV, or CVV results | | PAY_104 | Cryptographic error found in PIN | | PAY_103 | No financial impact | | PAY_102 | Already reversed (by Switch) | | PAY_101 | Blocked, first used—Transaction from new cardholder, and card not properly unblocked | | PAY_100 | Unsolicited reversal | | PAY_099 | Allowable number of PIN entry tries exceeded | | PAY_081 | Lost card, pick up (fraud account) | | PAY_096 | Exceeds withdrawal frequency limit | | PAY_095 | Transaction does not fulfill AML requirement | | PAY_094 | Security violation (source is not correct issuer) | | PAY_093 | Restricted card (card invalid in this region or country) | | PAY_092 | Exceeds approval amount limit | | PAY_091 | Suspected fraud | | PAY_090 | Transaction not allowed at terminal | | PAY_089 | Transaction not permitted to cardholder | | PAY_088 | Incorrect PIN or PIN missing | | PAY_087 | Expired card or expiration date is missing | | PAY_086 | No savings account | | PAY_085 | No checking account | | PAY_084 | Not sufficient funds | | PAY_083 | Closed account | | PAY_082 | Stolen card, pick up (fraud account) | | PAY_080 | No credit account | | PAY_079 | File temporarily not available for update or inquiry | | PAY_078 | Unable to locate record in file | | PAY_077 | No action taken | | PAY_076 | Re-enter transaction | | PAY_075 | No such issuer | | PAY_074 | Invalid account number (no such number) | | PAY_073 | Invalid amount or currency conversion field overflow | | PAY_072 | Invalid transaction | | PAY_071 | Approved (V.I.P) | | PAY_070 | Partial approval | | PAY_069 | Pick up card, special condition (fraud account) | | PAY_068 | Error | | PAY_067 | Do not honor | | PAY_132 | Pick up card (no fraud) | | PAY_133 | Invalid merchant | | PAY_064 | Refer to card issuer, special condition | | PAY_063 | Refer to card issuer | | PAY_062 | Business Operation is Invalid | | GE_008 | Notification not found | | GE_007 | Notification already exists | | GE_006 | Authentication method is invalid | | GE_005 | Event is invalid | | GE_004 | Bearer authentication requires username, a password and url token authentication | | GE_003 | Basic authentication requires both a username and a password | | PAY_066 | Transaction rejected by gateway analysis | | PAY_065 | Invalid request | | PAY_061 | Provider Requested Error | | CAD_006 | Tokenization card rejected by rules | | GE_002 | Parameter is invalid | | PAY_060 | Invalid external id format | | SE_003 | Invalid header token | | PAY_059 | AVS partial approved | | PAY_058 | AVS does not match | | PAY_057 | AVS unknow | | PAY_056 | Email verification failed | | PAY_055 | Email verification unknown | | PAY_054 | Telephone verification failed | | PAY_053 | Telephone verification unknown | | PAY_052 | Postal code verification failed | | PAY_051 | Postal code verification unknown | | PAY_050 | Cardholder name verification failed | | PAY_049 | Cardholder name verification unknown | | PAY_048 | Address verification failed | | PAY_047 | Address verification unknown | | PAY_046 | CVV does not match | | PAY_045 | CVV unkown | | PAY_044 | Revocation of all authorizations order | | PAY_043 | Revocation of authorization order | | PAY_042 | Rejected by card issuer | | PAY_041 | Security code invalid | | PAY_040 | Security Rules Violated | | PAY_039 | Unknown card | | PAY_038 | Card blocked | | PAY_037 | Restricted card | | PAY_036 | Illegal transaction | | PAY_035 | Invalid security code | | PAY_034 | Limit exceeded | | PAY_033 | Stolen card, pick up | | PAY_032 | Lost card | | PAY_031 | Security code expired | | PAY_030 | Fraud suspicion | | PAY_029 | Card expired | | PAY_028 | Impossible Reference Number | | PAY_027 | Access denied | | PAY_026 | Annulation by client | | PAY_025 | Invalid card issuer | | PAY_024 | Invalid requested amount | | PAY_023 | Hold card | | PAY_022 | Rejected by provider | | SE_002 | Invalid header token | | CARD_010 | Invalid request | | PAY_021 | Invalid currency code | | PAY_020 | Amount cannot be less or equal to zero | | PAY_019 | Invalid IP Address | | PAY_018 | Capture amount cannot be negative | | PAY_017 | Capture amount cannot be greater than the original amount | | PAY_015 | Invalid currency | | PAY_014 | No payment found to void | | PAY_013 | No payment found to void | | PAY_012 | No payment found to refund with external payment id | | PAY_011 | Payment duplicated for externalPaymentId | | PAY_010 | No payment founded to capture with external payment id | | PAY_009 | Payment with external id already captured | | PAY_008 | Country not found | | PAY_007 | There isn't parameter to agent and event | | PAY_006 | Payment method ID is null for description | | PAY_005 | Payment method not found for description | | PAY_004 | Credential not found | | PAY_003 | Key not found for agent | | PAY_002 | Agent not found | | PAY_001 | No router found for agent and payment method | | CA_007 | Card not found, invalid token | | CA_006 | Card not found, invalid previous payment code | | PAY_016 | Insufficient balance to refund | | SE_001 | Unauthorized request | | CAD_005 | Secure code is invalid | | CAD_004 | Expiration date is invalid | | CAD_003 | Invalid cardholder name | | CAD_002 | Pan is invalid | | CAD_001 | Origin not allowed | --- description: >- The Inyo Gateway tracks payments through well-defined lifecycle statuses. --- # Payment Status ## Status Lifecycle ``` CHALLENGE ──→ AUTHORIZED ──→ CAPTURED ──→ REFUNDED │ │ │ ↓ ↓ ↓ DECLINED VOIDED PARTIALLY_REFUNDED PARTIALLY_CAPTURED ``` ## Status Reference | Status | Description | Next Actions | |---|---|---| | **CHALLENGE** | 3DS verification required — cardholder must complete authentication | Wait for `AUTHORIZED` or `DECLINED` | | **AUTHORIZED** | Payment approved, funds reserved on the card | [Capture](../payment/pulling-funds/cards/capture.md) or [Void](../payment/pulling-funds/cards/void.md) | | **CAPTURED** | Funds settled to merchant account | [Refund](../payment/pulling-funds/cards/refund.md) (full or partial) | | **PARTIALLY_CAPTURED** | A portion of the authorized amount was captured | [Refund](../payment/pulling-funds/cards/refund.md) the captured portion | | **VOIDED** | Authorization cancelled before capture — funds released to cardholder | Terminal state | | **REFUNDED** | Full captured amount returned to cardholder | Terminal state | | **PARTIALLY_REFUNDED** | A portion of the captured amount was refunded | Additional [refunds](../payment/pulling-funds/cards/refund.md) possible | | **DECLINED** | Payment rejected by issuer, network, or fraud rules | Retry with different method | ## Key Rules - **AUTHORIZED → VOIDED**: Only before capture. Voids release the hold on the card. - **AUTHORIZED → CAPTURED**: Must happen within 7 days, or the authorization expires and is auto-voided. - **CAPTURED → REFUNDED**: Can be full or partial. Multiple partial refunds are allowed. - **DECLINED**: Terminal — the payment cannot be retried with the same `externalPaymentId`. --- description: >- Test data index for the Inyo Payment Gateway sandbox environment — test card numbers and simulation codes for integration testing. --- # Test Data Sandbox test data for simulating different transaction scenarios: - [**Test Cards**](cards.md) — Card numbers for approvals, declines, 3DS, AVS, and CVV testing --- description: >- Sandbox test card numbers for simulating authorization outcomes: successful approval, decline, 3DS challenge required, and various AVS/CVC scenarios. --- # Cards Test cards - Without 3DS | Number | Description | | ---------------- | ---------------- | | 5163613613613613 | Mastercard Debit | | 5555555555554444 | Mastercard | | 5454545454545454 | Mastercard | | 2221000000000009 | Mastercard | | 4462030000000000 | Visa Debit | | 4444333322221111 | Visa | | 4911830000000 | Visa | | 4917610000000000 | Visa | | 343434343434343 | Amex | | 6011000400000000 | Discover | | 36700102000000 | Diners | Test cards - With 3DS | Number | Description | | ---------------- | ---------------- | | 5413330033003303 | Mastercard Debit | | 5169527513596963 | Mastercard | | 4462030000000000 | Visa Debit | | 4983305199046950 | Visa | | 6011000400000000 | Discover | Test cards - With Data-Only 3DS | Number | Description | | ---------------- | ----------- | | 5454545454545454 | Mastercard | | 4975303994654672 | Visa | | 6011926557021045 | Discover | In order to simulate acquirer authorization error codes and validations, please refer to the table: | Name | Result | | ---------- | --------------------------------------------- | | AUTHORISED | HTTP 200 | | REFUSED | HTTP 400 | | ERROR | HTTP 400 | | REFUSED4 | HTTP 400, Hold card | | REFUSED5 | HTTP 400, General refuse | | REFUSED13 | HTTP 400, Invalid amount | | REFUSED33 | HTTP 400, Card Expired | | REFUSED34 | HTTP 400, Suspicious activity, possible fraud | | REFUSED41 | HTTP 400, Lost Card | | REFUSED43 | HTTP 400, Stolen Card | | REFUSED55 | HTTP 400, Invalid CVV | | REFUSED76 | HTTP 400, Card blocked | | REFUSED85 | HTTP 400, Rejected by the card issuer | CVV and AVS test values for Mastercard/Visa/Diners/Discover | Name | Result | | --------- | -------------------------------------------- | | < empty > | B - CVV/CVC not supplied by shopper/merchant | | 111 | C - CVV/CVC not checked | | 222 | C - CVV/CVC not checked | | 333 | C - CVV/CVC not checked | | 444 | D - CVV/CVC not matched | | 555 | A - CVV/CVC matched | CVV and AVS test values for Amex | Name | Result | | --------- | -------------------------------------------- | | < empty > | B - CVV/CVC not supplied by shopper/merchant | | 1111 | C - CVV/CVC not checked | | 2222 | C - CVV/CVC not checked | | 3333 | C - CVV/CVC not checked | | 4444 | D - CVV/CVC not matched | | 5555 | C - CVV/CVC not checked | | 6666 | A - CVV/CVC matched | AVS (Address verification system), that matches cardholder billing information | Name | Result | Description | | ------------- | ------------------------- | ----------------------------------------------------- | | AAAA or AAAAA | APPROVED | Postcode and address matched | | BBBB or BBBBB | PARTIAL APPROVED | Postcode matched; address not checked | | CCCC or CCCCC | PARTIAL APPROVED | Postcode matched; address not matched | | DDDD or DDDDD | PARTIAL APPROVED | Address matched; postcode not checked | | EEEE or EEEEE | NOT SENT TO ACQUIRER | Postcode and address not checked | | FFFF or FFFFF | PARTIAL APPROVED | Address matched; postcode not matched | | GGGG or GGGGG | PARTIAL APPROVED | Postcode not checked; address not matched | | HHHH or HHHHH | NOT SUPPLIED BY SHOPPER | Postcode and address not supplied by shopper/merchant | | IIII or IIIII | PARTIAL APPROVED | Address not checked; postcode not matched | | JJJJ or JJJJJ | FAILED | Postcode and address not matched | | < empty > | NOT SUPPLIED BY SHOPPER | Postcode and address not supplied by shopper/merchant | | KKKK or KKKKK | NO RESPONSE FROM ACQUIRER | Postcode and address not checked | | LLLL or LLLLL | NOT CHECKED BY ACQUIRER | Postcode and address not checked | | MMMM or MMMMM | UNKNOWN | Postcode and address not checked | --- description: >- Accept remote payments via Payment Link — generate a payment link via API, deliver it to customers via SMS or WhatsApp, and receive real-time results via webhook. Designed for AFT programs including money transfer, top-up, bill pay, and prepaid card loading. --- # Payment Link Payment Link is a remote payment solution designed for AFT (Account Funding Transaction) programs. It enables customers to fund money transfers, top-ups, bill payments, prepaid card loads, and wallet balances without being physically present. A payment link is generated via the API and delivered to the customer's phone via SMS or WhatsApp. The customer completes the payment electronically, and the result is sent back to your system in real time via webhook. *** ### How It Works ``` 1. Your backend calls the API → A payment link is generated 2. Link sent to customer → Delivered via SMS or WhatsApp 3. Customer opens the link → Secure payment page loads on their device 4. Customer completes payment → Card details entered and processed in real time 5. Result sent via webhook → Your system receives the payment outcome immediately ``` *** ### Key Features | Feature | Description | |---|---| | **AFT-Optimized** | Built for money transfer, top-up, bill pay, prepaid card loading, and wallet funding programs | | **API-Generated Links** | Create payment links programmatically — no manual intervention required | | **SMS & WhatsApp Delivery** | Reach customers on their preferred channel | | **Real-Time Processing** | Payments are processed electronically as soon as the customer pays | | **Webhook Notifications** | Receive instant payment results via HTTP webhook callbacks | | **Secure Checkout** | Customer-facing payment page is PCI-compliant and supports 3D Secure | | **No App Required** | Customers pay directly from their phone browser — no app download needed | *** ### Use Cases - **Money transfer** — Customer receives a payment link to fund a remittance or P2P transfer remotely - **Top-up & bill pay** — Send a payment link for mobile top-ups or utility bill payments - **Prepaid card loading** — Customers load funds onto prepaid cards via a secure link - **Wallet funding** — Enable wallet top-ups without requiring the customer to visit a physical location - **Agent-assisted transactions** — Agent generates a payment link during a call and the customer pays from their phone *** ### Better Together Payment Link works alongside the [Payments Gateway](README.md) and [Smart Terminal](smart-terminal.md) to provide a complete AFT payment platform: | Channel | Solution | |---|---| | **API / Online** | Payments Gateway API | | **Remote / Mobile** | Payment Link | | **In-person / Card present** | Smart Terminal | Using all three products on the same platform increases authorization rates and revenue by leveraging shared routing intelligence, fraud prevention, and transaction data across all channels. *** ### Getting Started Payment Link API documentation is coming soon. Contact [support@inyoglobal.com](mailto:support@inyoglobal.com) to get started with Payment Link or to request API access. --- description: >- Process card-present payments using the Inyo Smart Terminal — supports NFC tap-to-pay and manual card entry for AFT programs including money transfer, top-up, bill pay, and prepaid card loading. --- # Smart Terminal The Inyo Smart Terminal is a comprehensive card-present payment solution designed for AFT (Account Funding Transaction) programs. It enables agents and merchants to process in-person payments for money transfers, top-ups, bill payments, prepaid card loads, and wallet funding. The terminal supports NFC (Near Field Communication) for contactless tap-to-pay transactions, as well as manual card entry for situations where contactless is not available. *** ### Key Features | Feature | Description | |---|---| | **AFT-Optimized** | Built for money transfer, top-up, bill pay, prepaid card loading, and wallet funding programs | | **NFC Tap-to-Pay** | Accept contactless payments from cards, phones, and wearables | | **Manual Card Entry** | Process transactions by manually entering card details when needed | | **Card-Present Rates** | Benefit from lower interchange fees associated with card-present transactions | | **Smart Routing** | Transactions are routed through the same intelligent gateway for optimal authorization rates | | **Unified Platform** | Manage in-person and remote payments from a single platform | | **Real-Time Processing** | Transactions are authorized and processed immediately | *** ### How It Works ``` 1. Customer presents card → Tap (NFC) or manual entry on the terminal 2. Terminal reads card data → Securely captured and encrypted at the point of interaction 3. Transaction processed → Routed through the Inyo gateway for authorization 4. Result displayed → Approval or decline shown on the terminal in real time 5. Webhook notification → Your backend receives the transaction result ``` *** ### Use Cases - **Money transfer agents** — Customers walk in to fund a remittance and pay at the terminal via NFC or manual entry - **Top-up & bill pay locations** — Accept card payments for mobile top-ups and utility bill payments at agent locations - **Prepaid card loading** — Customers load funds onto prepaid cards at physical points of service - **Wallet funding** — Accept in-person card payments to fund digital wallets - **Multi-channel agents** — Combine the terminal with Payment Link for customers who prefer to pay remotely *** ### Better Together Smart Terminal works alongside the [Payments Gateway](README.md) and [Payment Link](payment-link.md) to provide a complete AFT payment platform: | Channel | Solution | |---|---| | **API / Online** | Payments Gateway API | | **Remote / Mobile** | Payment Link | | **In-person / Card present** | Smart Terminal | Running all three products on the same platform increases authorization rates and revenue. The gateway's smart routing, fraud signals, and transaction intelligence are shared across all channels — meaning an in-person transaction history can improve remote authorization decisions and vice versa. *** ### Getting Started Smart Terminal documentation is coming soon. Contact [support@inyoglobal.com](mailto:support@inyoglobal.com) to get started with Smart Terminal or to request access. --- description: >- Inyo Remittances API — cross-border money transfer infrastructure supporting 165+ countries. Covers sender KYC, FX quotes, recipient management, and transaction execution. --- # Remittances The Remittances API provides a complete, end-to-end solution for building cross-border payment applications. It combines Inyo's **Core Services** (regulatory infrastructure and money transmission licensing), **Compliance Platform** (KYC, KYB, AML, and anti-fraud), and **Payment Gateway** (fund collection and disbursement) into a single, unified integration. *** ### How It Works A remittance transaction follows a well-defined lifecycle that ensures regulatory compliance at every step: ``` ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Sender │ │ Compliance │ │ Quote │ │ Recipient │ │ Onboarding │───▶│ Verification │───▶│ (FX Rate) │───▶│ Setup │ │ (KYC) │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └──────────────┘ └─────────────┘ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │ Receipt │ │ Transaction │ │ Funding │◀─────────┘ │ (Regulated) │◀───│ Execution │◀───│ Source │ │ │ │ │ │ (Card/ACH) │ └─────────────┘ └─────────────┘ └──────────────┘ ``` *** ### Integration Steps | Step | Action | Endpoint | Description | | ---- | ------ | -------- | ----------- | | 1 | [Sender Onboarding](sender/) | `POST /people` | Create the sender and trigger KYC/AML screening | | 2 | [Compliance Check](sender/trust-level-limits.md) | `GET /participants/{id}/complianceLevels` | Verify the sender has reached at least Level 1 | | 3 | [Destinations](data-population/payout-countries-enabled.md) | `GET /payout/{country}/destinations` | Fetch available payout corridors | | 4 | [Get a Quote](quotes-fx.md) | `POST /payout/quotes` | Lock in FX rate and fees | | 5 | [Recipient Setup](recipient.md) | `POST /people` | Create the beneficiary using country-specific schemas | | 6 | [Recipient Account](recipient-account.md) | `POST /payout/participants/{id}/recipientAccounts/gateway` | Link the recipient's bank account | | 7 | [Funding Source](sender-funding-account.md) | `POST /payout/participants/{id}/fundingAccounts` | Register the sender's card or ACH account | | 8 | [Limits Check](sender/trust-level-limits.md) | `GET /fx/participants/{id}/limits` | Verify the sender hasn't exceeded transaction limits | | 9 | [Execute Transaction](transaction.md) | `POST /fx/transactions` | Submit the transaction with all linked IDs | | 10 | [Webhooks](webhooks.md) | `POST /webhooks` | Register for real-time status notifications | *** ### Architecture Pattern Inyo requires a **Backend-for-Frontend (BFF)** architecture. Your API credentials (`x-api-key`, `x-agent-id`, `x-agent-api-key`) must **never** be exposed to frontend or mobile clients. ``` ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Frontend │────▶│ Your Server │────▶│ Inyo API │ │ (React, etc) │ │ (BFF Layer) │ │ (Sandbox / │ │ │◀────│ │◀────│ Production) │ └──────────────┘ └──────────────┘ └──────────────┘ Holds API keys KYC, FX, Payout Orchestrates calls Compliance engine ``` *** ### Caching Guidelines The Inyo API enforces rate limits on data-population endpoints. Implement caching as follows: | Data | Cache Duration | Reason | | ---- | -------------- | ------ | | Destinations & banks | 24 hours | Rarely change | | Compliance limits | 24 hours (or until a transaction occurs) | Semi-static | | Recipient/account schemas | 24 hours | Rarely change | | Quotes | **Never cache** | Expire within ~2 minutes | *** ### Transaction Types The platform supports three transaction types: | Type | Description | Use Case | | ---- | ----------- | -------- | | `FX` | Cross-border foreign exchange transaction | Remittances, international payouts | | `TOP_UP` | Airtime or mobile top-up | Telecom recharges | | `BILLPAY` | Bill payment | Utility and service payments | *** ### Need Help? * **Reference implementation**: [Remittance Sample Project](https://github.com/inyo-global/remittance-sample) — a working Node.js + React app demonstrating the full integration * **OpenAPI specification**: [Interactive API docs](https://dev-api.inyoglobal.com/sandbox/) * **Support**: Get in touch with our sales team --- description: >- Step-by-step quickstart for the Inyo Remittances API — create a sender, get an FX quote, set up a recipient, and send your first sandbox transaction. --- # Getting Started This guide walks you through sending your first cross-border transaction on the Inyo sandbox. By the end, you'll have a working remittance from a US sender to an international recipient. *** ### Prerequisites Before you begin, ensure you have: * **Sandbox credentials** provided by Inyo during onboarding: * `tenant` — Your organization identifier * `x-api-key` — Primary API key (Tenant-level) * `x-agent-id` — Your agent UUID * `x-agent-api-key` — Agent-level API key * **Node.js v18+** (if running the [sample project](https://github.com/inyo-global/remittance-sample)) * A REST client (cURL, Postman, or similar) > 📬 Don't have credentials yet? Get in touch with our sales team to request sandbox access. *** ### Sandbox Environment | Resource | URL | | ------------------ | ---------------------------------------------------------------------------------- | | Core API (sandbox) | `https://api.sandbox.inyoplatform.com` | | OpenAPI docs | [https://dev-api.inyoglobal.com/sandbox/](https://dev-api.inyoglobal.com/sandbox/) | | Developer portal | [https://dev.inyoglobal.com](https://dev.inyoglobal.com) | *** ### Quick Start: Your First Transaction This walkthrough covers the minimum steps to execute a USD → BRL remittance in the sandbox. #### Step 1: Create the Sender Register the person who will send money. This triggers KYC/AML screening. ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "birthDate": "1990-01-15", "phoneNumber": "+15551234567", "gender": "Male", "address": { "countryCode": "US", "stateCode": "CA", "city": "San Francisco", "line1": "123 Market St", "zipcode": "94105" }, "documents": [ { "type": "SSN", "document": "123456789", "countryCode": "US" } ] }' ``` > ⚠️ **Save the returned `id`** — this is the `senderId` used throughout the transaction lifecycle. #### Step 2: Verify Compliance Level Confirm the sender has reached at least **Level 1** before proceeding. ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/participants/$SENDER_ID/complianceLevels \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` If the response shows `LEVEL_0`, the sender is missing required fields. Check the `nextLevel.requiredFields` array to see what's needed. #### Step 3: Get Available Destinations ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/us/destinations \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` This returns the list of countries your tenant is enabled to send to, along with their currencies. #### Step 4: Lock in an FX Quote ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/quotes \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "fromCurrency": "USD", "toCurrency": "BRL", "amount": 100.00 }' ``` > ⚠️ **Save the `quoteId`** from the response. Quotes expire in \~2 minutes. #### Step 5: Create the Recipient First, fetch the required schema for the destination country: ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/recipients/schema/br \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` Then create the recipient using the schema-required fields: ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "firstName": "Maria", "lastName": "Silva", "phoneNumber": "+5511999998888", "documents": [ { "type": "CPF", "document": "12345678901", "countryCode": "BR" } ] }' ``` > ⚠️ **Save the returned `id`** — this is the `recipientId`. #### Step 6: Link the Recipient's Bank Account Fetch the account schema, then link the bank account: ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/gateway \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "recipient-acct-001", "asset": "BRL", "payoutMethod": { "type": "BANK_DEPOSIT", "countryCode": "BR", "bankCode": "001", "routingNumber": "0001", "accountNumber": "123456", "accountType": "CHECKING" } }' ``` > ⚠️ **Save the returned `id`** — this is the `recipientAccountId`. #### Step 7: Register the Sender's Funding Source You can fund transactions with either a **debit card** or an **ACH bank account** (via Plaid). **Option A: Debit Card** ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "funding-001", "asset": "USD", "nickname": "My Debit Card", "paymentMethod": { "type": "CARD", "ipAddress": "127.0.0.1", "token": "tok_sandbox_test_token", "bin": "541333", "schemeId": "MASTERCARD", "lastFourDigits": "3303", "billingAddress": { "countryCode": "US", "stateCode": "CA", "city": "San Francisco", "line1": "123 Market St", "zipcode": "94105" } } }' ``` **Option B: ACH (via Plaid)** First, create a Plaid link token, then complete the Plaid Link flow on the client to get the `accountCheckId` and `accountCheckToken`. See [Sender Funding Account](./sender-funding-account.md) for the full ACH flow. ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "funding-002", "asset": "USD", "nickname": "My Checking Account", "paymentMethod": { "type": "ACH", "countryCode": "US", "accountCheckId": "PLAID_ACCOUNT_ID", "accountCheckToken": "PLAID_PUBLIC_TOKEN" } }' ``` > **Save the returned `id`** — this is the `fundingAccountId`. #### Step 8: Execute the Transaction Link all the IDs together and submit: ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "senderId": "'$SENDER_ID'", "recipientId": "'$RECIPIENT_ID'", "fundingAccountId": "'$FUNDING_ACCOUNT_ID'", "recipientAccountId": "'$RECIPIENT_ACCOUNT_ID'", "quoteId": "'$QUOTE_ID'", "additionalData": {}, "deviceData": { "userIpAddress": "127.0.0.1" } }' ``` A successful response returns HTTP `202` with the full transaction object, including `complianceStatus`, `payoutStatus`, exchange rate details, and the `receipt` object containing legally required disclosures. *** ### What's Next? * [Set up webhooks](webhooks.md) to receive real-time transaction status updates * Review the [transaction lifecycle](transaction.md) to understand status transitions * Explore [compliance levels](sender/trust-level-limits.md) to build progressive KYC flows * Check [test data](sender/test-data.md) for sandbox testing scenarios *** ### Common Issues | Symptom | Cause | Fix | | ----------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------- | | `401 Unauthorized` | Invalid API keys | Verify `x-api-key`, `x-agent-id`, and `x-agent-api-key` in your `.env` | | `403 Forbidden` | Agent not approved, or sender below compliance Level 1 | Check agent approval status; verify sender's compliance level | | `400 Missing fields` | Incomplete sender/recipient profile | Check schema endpoints for required fields per country | | `422 Unprocessable` | Quote expired, limit exceeded, or invalid references | Refresh the quote; check limits via `GET /fx/participants/{id}/limits` | | `429 Too Many Requests` | Rate limit exceeded | Implement caching for destinations, banks, and schemas (24h TTL) | *** ### Feedback & Support We're continuously improving these docs. For feedback, questions, or technical issues: * 📧 Get in touch with our sales team * 📖 [Interactive API reference](https://dev-api.inyoglobal.com/sandbox/) * 💻 [Sample project on GitHub](https://github.com/inyo-global/remittance-sample) --- description: >- OAuth 2.0 Client Credentials authentication for the Remittances API — obtaining Bearer tokens and handling token expiry. --- # Authentication Inyo uses API keys to securely control access to its APIs. All requests must be authenticated — unauthenticated requests are rejected by the firewall. *** ### Credentials During onboarding, Inyo provides you with the following credentials: | Credential | Header | Description | | ---------- | ------ | ----------- | | Tenant ID | Path parameter (`{tenant}`) | Your organization identifier. Used in all endpoint URLs. | | Tenant API Key | `x-api-key` | Primary key for account management, compliance queries, and listing operations. | | Agent ID | `x-agent-id` | UUID identifying the agent that executes transactional operations. | | Agent API Key | `x-agent-api-key` | Secret key paired with the agent, used for quotes, transactions, and payouts. | *** ### Authentication Layers The API uses a **dual-layer authentication model**. Different endpoints require different credential combinations: #### Tenant-Level Authentication Used for account management, compliance, and read operations. ```http GET /organizations/{tenant}/people/{personId} Content-Type: application/json Accept: application/json x-api-key: {your_tenant_api_key} ``` **Endpoints using Tenant auth:** * Person/Company CRUD (`POST /people`, `PATCH /people/{id}`, etc.) * Compliance levels (`GET /participants/{id}/complianceLevels`) * Transaction listing and status (`GET /fx/transactions`, `GET /fx/transactions/{id}/status`) * Document uploads (`POST /people/{id}/documents/...`) * Webhook management (`POST /webhooks`, `GET /webhooks`) * Transaction cancel/refund operations * Address verification #### Agent-Level Authentication Used for transactional operations that move money. Requires **both** the Tenant API key and Agent credentials. ```http POST /organizations/{tenant}/fx/transactions Content-Type: application/json Accept: application/json x-api-key: {your_tenant_api_key} x-agent-id: {your_agent_id} x-agent-api-key: {your_agent_api_key} ``` **Endpoints using Agent auth:** * Quotes (`POST /payout/quotes`) * Transactions (`POST /fx/transactions`) * Funding accounts (`POST /payout/participants/{id}/fundingAccounts`) * Recipient accounts (`POST /payout/participants/{id}/recipientAccounts/gateway`) * Wallet operations > ⚠️ **Agent Approval Required**: Newly created agents must be approved by the Inyo compliance team before they can execute transactions. Before approval, agent-authenticated requests will return `403 Forbidden`. *** ### Required Headers Include the following headers on **every** request: ```http Content-Type: application/json Accept: application/json x-api-key: {your_tenant_api_key} ``` For agent-authenticated endpoints, also include: ```http x-agent-id: {your_agent_id} x-agent-api-key: {your_agent_api_key} ``` *** ### Environments | Environment | Base URL | | ----------- | -------- | | Sandbox | `https://api.sandbox.inyoplatform.com` | | Production | Provided during go-live (contact your Inyo account manager) | > 💡 **Sandbox and production use separate credentials.** Never use sandbox keys in production or vice versa. *** ### Security Best Practices * **Never expose API keys in frontend code.** Use a Backend-for-Frontend (BFF) pattern — your server holds the secrets and proxies requests to Inyo. * **Rotate keys** if you suspect compromise. Contact our sales team for key rotation. * **Restrict network access** — use IP allowlisting in production if supported by your plan. * **Use HTTPS only** — all Inyo endpoints enforce TLS. Plain HTTP requests are rejected. *** ### Error Responses | HTTP Status | Meaning | | ----------- | ------- | | `401 Unauthorized` | Missing or invalid `x-api-key`. | | `403 Forbidden` | Valid key but insufficient permissions — agent not approved, or endpoint requires agent auth. | | `429 Too Many Requests` | Rate limit exceeded. Implement caching and backoff. | --- description: >- Reference data endpoints for remittance integrations — country lists, bank directories, and country-specific schemas for recipients and accounts. --- # Data Population Before building transaction flows, you need to populate your application with reference data from the Inyo API. This data includes available payout countries, required schemas for recipients and accounts, and bank lists. *** ### Overview | Endpoint | Purpose | Cache Duration | | -------- | ------- | -------------- | | [Payout Countries](payout-countries-enabled.md) | Available destination countries for a source corridor | 24 hours | | [Recipient Schema](recipient-schema.md) | Required fields to create a recipient in a given country | 24 hours | | [Recipient Account Schema](recipient-account-schema.md) | Required fields to link a bank account in a given country | 24 hours | | [Transaction Schema](transaction-schema.md) | Country-specific `additionalData` fields for transactions | 24 hours | | [Banks in a Country](banks-in-a-country.md) | List of banks available for a destination country | 24 hours | *** ### Rate Limits All data population endpoints are subject to strict rate limits. These endpoints serve reference data that changes infrequently — you are expected to **cache responses locally** and refresh periodically (recommended: every 24 hours). **Do not call these endpoints on every transaction.** Excessive calls will result in `429 Too Many Requests` responses. *** ### Recommended Caching Strategy ```javascript // Example: Cache with 24-hour TTL const NodeCache = require('node-cache'); const cache = new NodeCache({ stdTTL: 86400 }); // 24 hours async function getDestinations(sourceCountry) { const cacheKey = `destinations:${sourceCountry}`; let data = cache.get(cacheKey); if (!data) { data = await inyoApi.get(`/payout/${sourceCountry}/destinations`); cache.set(cacheKey, data); } return data; } ``` *** ### Schema-Driven Integration A key design principle of the Inyo API is that **data requirements vary by country**. Instead of hardcoding field requirements, your integration should: 1. **Fetch the schema** for the destination country 2. **Dynamically render forms** based on required/optional fields 3. **Validate user input** against the schema before submitting This approach ensures your application automatically adapts when Inyo adds new countries or changes requirements — without code changes on your side. ``` User selects destination country │ ▼ Fetch recipient schema ──────────▶ Build person form Fetch account schema ────────────▶ Build bank account form Fetch bank list (if needed) ─────▶ Populate bank dropdown Fetch transaction schema ────────▶ Collect additionalData fields ``` --- description: >- List of countries enabled for international remittance payouts, along with supported payout methods (bank transfer, wallet, card) per country. --- # Payout countries enabled The Payout Destinations endpoint returns the list of countries available to receive payouts from a specified source country. This is typically used to determine which destination countries are supported for a payout corridor (that are active for your installation) before proceeding with quote or transaction creation. | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | ----------------------------------------------------------------- | | tenant | string | Yes | The tenant identifier for your organization. | | sourceCountryIso2 | string | Yes | ISO 3166-1 alpha-2 code of the source country (e.g., US, BR, MX). | ### Sample Request {% tabs %} {% tab title="Request" %} ``` curl --request GET \ --url {api_endpoint}/organizations/{tenant}/payout/{sourceCountryIso2}/destinations \ --header 'content-type: application/json' \ --header 'x-agent-api-key: {agentApiKey}' \ --header 'x-agent-id: {agentId}' ``` {% endtab %} {% tab title="Response" %} ``` { "countryDestinations": [ { "country": "BR", "countryName": "Brazil", "currency": "BRL", "currencyName": "Brazilian real", "currencySymbol": "R$", "emoji": "" }, { "country": "DO", "countryName": "Dominican Republic", "currency": "DOP", "currencyName": "Dominican peso", "currencySymbol": "RD$", "emoji": "" } ] } ``` {% endtab %} {% endtabs %} ### Response Details * country — ISO 3166-1 alpha-2 code of the destination country. * countryName — Full name of the destination country. * currency — ISO 4217 code of the destination currency. * currencyName — Full name of the currency. * currencySymbol — Symbol for the destination currency (if available). * emoji — (Optional) Flag emoji representation. *** ### Usage Notes * Use this endpoint to populate dropdown lists or validate corridors when the user selects a source country. * Once a valid source–destination pair is chosen, you can proceed to request quotes and create payouts. [Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Payout-Resource/paths/~1organizations~1%7Btenant%7D~1payout~1%7BsourceCountryIso2%7D~1destinations/get) ``` GET /organizations/{tenant}/payout/{sourceCountryIso2}/destinations ``` --- description: >- Country-specific required fields for creating recipient entities — vary by destination country and payout method. --- # Recipient schema Each country requires a set of different information for the recipient, and that schema is described by | Parameter | Type | Required | Description | | ---------------------- | ------ | -------- | ---------------------------------------------------------------------- | | tenant | string | Yes | The tenant identifier for your organization. | | destinationCountryIso2 | string | Yes | ISO 3166-1 alpha-2 code of the destination country (e.g., US, BR, MX). | ### Sample Request {% tabs %} {% tab title="Request" %} ``` curl --request GET \ --url {api_endpoint}/organizations/{tenant}/payout/recipients/schema/{destinationCountryIso2} \ --header 'content-type: application/json' \ --header 'x-agent-api-key: {agentApiKey}' \ --header 'x-agent-id: {agentId}' ``` {% endtab %} {% tab title="Response" %} ``` { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Inyo Global Recipient Data - Colombia", "description": "Schema for a COL person.", "version": "1.0.0", "type": "object", "required": [ "firstName", "lastName", "address", "documents" ], "properties": { "firstName": { "type": "string", "description": "The first name of the person.", "minLength": 1 }, "lastName": { "type": "string", "description": "The last name of the person.", "minLength": 1 }, "birthDate": { "type": "string", "description": "The birth date of the person. Formatted as yyyy-MM-dd", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, "phoneNumber": { "type": "string", "description": "The contact phone number of the person.", "minLength": 1 }, "documents": { "type": "array", "description": "A list of identification documents for the person.", "minItems": 1, "items": { "type": "object", "required": [ "document", "type" ], "properties": { "document": { "type": "string", "description": "The document number." }, "type": { "type": "string", "description": "The type of document.", "enum": [ "CC" ] }, "countryCode": { "type": "string", "description": "The country code of document.", "enum": [ "CO" ] } }, "if": { "properties": { "type": { "const": "CC" } } }, "then": { "properties": { "document": { "description": "Cédula de Ciudadania", "pattern": "^(\\d){8,10}" } } } } } } } ``` {% endtab %} {% endtabs %} *** [Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Payout-Resource/paths/~1organizations~1%7Btenant%7D~1payout~1recipients~1schema~1%7BcountryIso2%7D/get) ``` GET /organizations/{tenant}/payout/recipients/schema/{destinationCountryIso2} ``` --- description: >- Country-specific required field schemas for recipient bank accounts and wallets — use before creating recipient accounts to ensure correct field submission. --- # Recipient account schema For a specific payout method, the schema can vary and the amount of information too. | Parameter | Type | Required | Description | | ---------------------- | ------ | -------- | ---------------------------------------------------------------------- | | tenant | string | Yes | The tenant identifier for your organization. | | destinationCountryIso2 | string | Yes | ISO 3166-1 alpha-2 code of the destination country (e.g., US, BR, MX). | ### Sample Request {% tabs %} {% tab title="Request" %} ``` curl --request GET \ --url {api_endpoint}/organizations/{tenant}/payout/recipientAccounts/schema/{destinationCountryIso2} \ --header 'content-type: application/json' \ --header 'x-agent-api-key: {agentApiKey}' \ --header 'x-agent-id: {agentId}' ``` {% endtab %} {% tab title="Response" %} ``` { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Inyo Global Recipient Account Data - Colombia", "description": "Schema for a COL account.", "version": "1.0.0", "type": "object", "required": [ "asset", "payoutMethod" ], "properties": { "externalId": { "type": "string", "description": "An unique identifier from your system." }, "asset": { "type": "string", "description": "The currency code that identifies the recipient account's currency in the 'ISO 4217' format.", "enum": [ "COP" ] }, "payoutMethod": { "type": "object", "description": "Details of the payout method.", "required": [ "countryCode", "bankCode", "routingNumber", "accountNumber", "accountType" ], "properties": { "type": { "type": "string", "description": "The type of payout method.", "enum": [ "BANK_DEPOSIT" ] }, "countryCode": { "type": "string", "description": "The country code of the bank account.", "enum": [ "CO" ] }, "bankCode": { "type": "string", "description": "The code of the bank.", "minLength": 1 }, "routingNumber": { "type": "string", "description": "The routing number for the bank (e.g., ABA for USA, SWIFT/BIC for international).", "minLength": 3 }, "accountNumber": { "type": "string", "description": "The bank account number.", "minLength": 1 }, "accountType": { "type": "string", "description": "The type of bank account.", "enum": [ "CHECKING", "SAVINGS" ] } } } } } ``` {% endtab %} {% endtabs %} *** [Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Payout-Resource/paths/~1organizations~1%7Btenant%7D~1payout~1recipientAccounts~1schema~1%7BcountryIso2%7D/get) ``` GET /organizations/{tenant}/payout/recipientAccounts/schema/{destinationCountryIso2} ``` --- description: >- Field requirements for creating remittance transactions by corridor and payment method — validate payload structure before submission. --- # Transaction schema Each country destination requires a set of different information for the additional data in transaction, and that schema is described by | Parameter | Type | Required | Description | | ---------------------- | ------ | -------- | ---------------------------------------------------------------------- | | tenant | string | Yes | The tenant identifier for your organization. | | destinationCountryIso2 | string | Yes | ISO 3166-1 alpha-2 code of the destination country (e.g., US, BR, MX). | ### Sample Request {% tabs %} {% tab title="Request" %} ``` curl --request GET \ --url '{api_endpoint}/organizations/{tenant}/fx/transactions/schema?countryCode={destinationCountryIso2}' \ --header 'content-type: application/json' \ --header 'x-agent-api-key: {agentApiKey}' \ --header 'x-agent-id: {agentId}' ``` {% endtab %} {% tab title="Response" %} ``` { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Inyo Global Payout Additional Data - Morocco", "version": "1.0.0", "type": "object", "properties": { "additionalData": { "type": "object", "properties": { "statementNarrative": { "type": "string", "description": "Optional narrative text providing transaction details." } } } }, "required": [ "additionalData" ] } ``` {% endtab %} {% endtabs %} [Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/fx_Transaction-Resource/paths/~1organizations~1%7Btenant%7D~1fx~1transactions~1schema/get) ``` GET /organizations/{tenant}/fx/transactions/schema?countryCode={destinationCountryIso2} ``` --- description: >- Retrieve the list of supported banks in a given country for bank-transfer remittance payouts, including bank codes and names. --- # Banks in a country For some country destinations we have a list of the available banks. | Parameter | Type | Required | Description | | ---------------------- | ------ | -------- | ---------------------------------------------------------------------- | | tenant | string | Yes | The tenant identifier for your organization. | | destinationCountryIso2 | string | Yes | ISO 3166-1 alpha-2 code of the destination country (e.g., US, BR, MX). | ### Sample Request {% tabs %} {% tab title="Request" %} ``` curl --request GET \ --url {api_endpoint}/organizations/{tenant}/payout/{destinationCountryIso2}/banks \ --header 'content-type: application/json' \ --header 'x-agent-api-key: {agentApiKey}' \ --header 'x-agent-id: {agentId}' ``` {% endtab %} {% tab title="Response" %} ```json { "total": 47, "size": 10, "page": 0, "numberOfPages": 5, "items": [ { "code": "007", "name": "BANCO BCSC", "countryIso2": "CO" }, { "code": "1000", "name": "BANCO REPÚBLICA", "countryIso2": "CO" }, { "code": "1001", "name": "BANCO DE BOGOTÁ", "countryIso2": "CO" } ... ] } ``` {% endtab %} {% endtabs %} [Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Payout-Resource/paths/~1organizations~1%7Btenant%7D~1payout~1%7BcountryIso2%7D~1banks/get) ``` GET /organizations/{tenant}/payout/{destinationCountryIso2}/banks ``` --- description: >- Request foreign exchange rate quotes and lock in exchange rates before creating a remittance transaction. Covers quote expiry and rate locking. --- # Quotes (FX) The FX Quote API allows you to request and lock in foreign exchange rates for a specific currency corridor and amount. The returned `quoteId` guarantees the rate for a fixed window, protecting both you and your customer from FX fluctuations during the transaction flow. *** ### How Pricing Works Two components determine the cost to the end user: * **FX Spread** — Your margin on the exchange rate, configured per corridor by Inyo during onboarding. * **Transaction Fee** — A per-transaction fee, also configured during onboarding. Depending on your contract, you may be able to override fees at the quote level (e.g., for promotional "zero fee" offers). #### Amount Types When requesting a quote, the `amountType` field controls how fees are applied: | Type | Behavior | Example (fee = $4, amount = $100) | | ---- | -------- | --------------------------------- | | `NET` | Fee is added on top of the amount. The full amount is converted. | Sender pays **$104**. $100 is converted to destination currency. | | `GROSS` | Fee is deducted from the amount. Only the remainder is converted. | Sender pays **$100**. $96 is converted to destination currency. | *** ### Requesting a Quote **Endpoint:** `POST /organizations/{tenant}/payout/quotes`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) #### Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `fromCurrency` | string | Yes | Source currency (ISO 4217), e.g., `USD` | | `toCurrency` | string | Yes | Destination currency (ISO 4217), e.g., `BRL` | | `amount` | number | Yes | Amount to convert | | `fee` | object | No | Override the default fee (if allowed by your contract) | | `fee.amount` | number | — | Fee amount | | `fee.currency` | string | — | Fee currency (typically matches `fromCurrency`) | | `amountType` | string | No | `GROSS` or `NET` (see above) | #### Sample Request ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/quotes \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "fromCurrency": "USD", "toCurrency": "BRL", "amount": 1000, "fee": { "amount": 4.99, "currency": "USD" }, "amountType": "NET" }' ``` #### Sample Response ```json { "quotes": [ { "id": "f28be3f9-ad4d-4a6c-a85d-ae5515a4a158", "agentId": "8500786a-68e9-45fd-b1d8-c590f1e06450", "fromAsset": "USD", "toAsset": "BRL", "effectiveRate": "5.39023000", "totalCost": { "amount": "5.23", "currency": "USD" }, "product": "ACCOUNT", "expireAt": "2025-09-15T11:39:01Z", "createdAt": "2025-09-15T11:37:01Z", "sourceAmount": { "amount": "1000.00", "currency": "USD" }, "destinationAmount": { "amount": "5390.23", "currency": "BRL" } } ] } ``` *** ### Response Fields | Field | Description | | ----- | ----------- | | `id` | Unique quote identifier. **Save this** — it's required for the transaction. | | `fromAsset` / `toAsset` | Source and destination currencies | | `effectiveRate` | The FX rate applied for this conversion (includes your spread) | | `sourceAmount` | The amount in the source currency | | `destinationAmount` | The amount the recipient will receive | | `totalCost` | Total fees/costs in the source currency | | `product` | Quote context: `ACCOUNT` (bank deposit), `WALLET`, or `CARD` | | `expireAt` | ISO 8601 timestamp when the quote becomes invalid | | `createdAt` | ISO 8601 timestamp when the quote was generated | *** ### Example Conversion Walkthrough **Scenario:** Convert 1,000 USD → BRL | Field | Value | | ----- | ----- | | Source Currency | USD | | Destination Currency | BRL | | Source Amount | 1,000.00 USD | | Effective Rate | 5.39023000 | | Destination Amount | 5,390.23 BRL | | Total Cost (fees) | 5.23 USD | | Product | ACCOUNT | | Quote Validity | ~2 minutes | *** ### Retrieving a Quote **Endpoint:** `GET /organizations/{tenant}/payout/quotes/{quoteId}`\ **Authentication:** Agent-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/quotes/$QUOTE_ID \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` Use this to check if a quote is still valid before submitting a transaction. *** ### Handling Quote Expiration Quotes have a short validity window (typically ~2 minutes). Follow these guidelines: * **Always check `expireAt`** before using a quote in a transaction. * **Refresh proactively** — request a new quote when ~90% of the validity period has elapsed. * **Never cache quotes** — unlike destinations and bank lists, quotes are time-sensitive and must be fetched fresh. * **Expired quotes will be rejected** — if you submit a transaction with an expired `quoteId`, the API returns a `400` or `422` error. #### Recommended UX Pattern ``` 1. User selects amount and destination → Request quote 2. Display rate, fees, and receive amount 3. Start a countdown timer based on expireAt 4. If timer reaches ~80%, show "Rate expiring" warning 5. If expired, auto-refresh and update the display 6. On "Confirm", immediately submit the transaction with the quoteId ``` *** ### All Endpoints | Operation | Method | Endpoint | | --------- | ------ | -------- | | Request quote | `POST` | `/organizations/{tenant}/payout/quotes` | | Get quote by ID | `GET` | `/organizations/{tenant}/payout/quotes/{quoteId}` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Quote-Resource) --- description: >- Sender identity management for remittances — KYC registration, trust level tiers, velocity limits, and compliance document uploads. --- # Sender The Sender is the participant who initiates and pays for a transaction. Every sender must pass through KYC (Know Your Customer) screening, and their transaction limits are determined by their compliance level. *** ### Initial Compliance Setup At the beginning of your integration, the Inyo compliance team works with your organization to define a custom compliance framework tailored to your product and customer profile. This framework determines: * **Compliance levels** — tiers that define how much a customer can send within set timeframes (24h, 30d, 180d) * **Validation rules** — the required data and documents for each level (e.g., name, SSN, proof of income) * **Risk controls** — thresholds that trigger enhanced due diligence This configuration is unique per tenant and directly impacts how participants are verified and which operations they can perform. *** ### Creating a Sender **Endpoint:** `POST /organizations/{tenant}/people`\ **Authentication:** Tenant-level (`x-api-key`) No fields are technically *required* to create a person — but to use them as a sender, they must reach at least **Compliance Level 1**, which typically requires first name, last name, address, and phone number. ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "birthDate": "1990-01-15", "phoneNumber": "+15551234567", "gender": "Male", "externalId": "your-internal-id-001", "address": { "countryCode": "US", "stateCode": "CA", "city": "San Francisco", "line1": "123 Market St", "zipcode": "94105" }, "documents": [ { "type": "SSN", "document": "123456789", "countryCode": "US" } ], "occupation": "Software Engineer" }' ``` #### Request Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `firstName` | string | For Level 1 | First name | | `lastName` | string | For Level 1 | Last name | | `phoneNumber` | string | For Level 1 | Phone with country code (e.g., `+15551234567`) | | `email` | string | For US persons | Email address | | `gender` | string | For US persons | `Male`, `Female`, or `Other` | | `birthDate` | string | For US persons | Format: `yyyy-MM-dd` | | `externalId` | string | No | Your internal reference ID | | `address` | object | For Level 1 | Residential address | | `address.countryCode` | string | Yes (in address) | ISO 3166-1 alpha-2 | | `address.stateCode` | string | For US | US state code (e.g., `CA`) | | `address.city` | string | Yes (in address) | City name | | `address.line1` | string | Yes (in address) | Street address | | `address.line2` | string | No | Additional address info | | `address.zipcode` | string | Yes (in address) | Postal code | | `documents` | array | For Level 2+ | Identity documents | | `documents[].type` | string | Yes (in doc) | `SSN`, `ITIN` (US), `CPF` (BR), etc. | | `documents[].document` | string | Yes (in doc) | Document number | | `documents[].countryCode` | string | Yes (in doc) | Issuing country | | `occupation` | string | For Level 2 | Person's occupation | | `employerName` | string | No | Employer name | #### Sample Response ```json { "id": "48066496-9445-41b7-acbe-85e069a77cb7", "firstName": "John", "lastName": "Doe", "mainAddressId": "9c2ea7e5-51a2-4ea1-83cf-8948754486f8", "phoneNumber": "+15551234567", "email": "john.doe@example.com", "gender": "Male", "birthDate": "1990-01-15", "externalId": "your-internal-id-001", "updatedAt": "2025-01-15T12:00:00", "documents": [], "occupation": "Software Engineer", "documentId": null, "sourceOfFundsId": null, "employerName": null, "employerAddressId": null } ``` > ⚠️ **Save the returned `id`** — this is the `senderId` used in all subsequent API calls. *** ### Updating a Sender **Endpoint:** `PATCH /organizations/{tenant}/people/{personId}`\ **Authentication:** Tenant-level Only fields included in the request body are updated; omitted fields remain unchanged. ```bash curl --request PATCH \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people/$PERSON_ID \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "occupation": "Consultant", "documents": [ { "type": "SSN", "document": "987654321", "countryCode": "US" } ] }' ``` *** ### Address Verification US addresses are automatically validated through USPS APIs. If validation fails, the sender's transactions will be placed **on hold** until the compliance team manually verifies the address. **Verify an address before submitting:** ```bash curl --request GET \ --url "https://api.sandbox.inyoplatform.com/organizations/$TENANT/addresses/check?countryCode=US&stateCode=CA&city=San+Francisco&line1=123+Market+St&line2=&zipCode=94105" \ --header "x-api-key: $API_KEY" ``` | Response Code | Meaning | | ------------- | ------- | | `202` | Address is valid | | `200` | Address suggestion returned (USPS found a corrected version) | | `400` | Address validation failed | *** ### Creating Business Senders (KYB) For B2B use cases, you can create a company as a sender: **Endpoint:** `POST /organizations/{tenant}/companies`\ **Authentication:** Tenant-level Companies follow a similar compliance level system but with different required fields (business registration, EIN, etc.). Contact your Inyo account manager for your tenant-specific KYB configuration. *** ### Compliance Levels | Level | Typical Required Fields | Description | | ----- | ----------------------- | ----------- | | Level 0 | (none) | Cannot transact | | Level 1 | firstName, lastName, address, phoneNumber | Basic KYC | | Level 2 | SSN, occupation, document ID | Enhanced KYC | | Level 3 | Proof of source of funds | Full KYC | > ⚠️ These are customizable per tenant. See [Trust Level Limits](trust-level-limits.md) for details. *** ### Best Practices * **Progressive onboarding** — Collect only Level 1 fields at signup. Prompt for more data when the user needs higher limits. * **Pre-validate addresses** — Use the address check endpoint before creating the sender to avoid holds. * **Sync compliance state** — After profile updates, re-check the compliance level to see if it has upgraded. * **Handle restricted users** — If a sender is `Restricted`, check `GET /participants/{id}/complianceLevels` to understand why and what action is needed. *** ### Related Pages * [Trust Level Limits](trust-level-limits.md) — Check and upgrade compliance levels * [Uploading Documents](uploading-documents.md) — Submit ID and source-of-funds documents * [Test Data](test-data.md) — Sandbox testing scenarios for compliance flows ### All Endpoints | Operation | Method | Endpoint | | --------- | ------ | -------- | | Create person | `POST` | `/organizations/{tenant}/people` | | Update person | `PATCH` | `/organizations/{tenant}/people/{personId}` | | Get person | `GET` | `/organizations/{tenant}/people/{personId}` | | Get person with details | `GET` | `/organizations/{tenant}/people/{personId}/details` | | Update address | `PUT` | `/organizations/{tenant}/people/{personId}/address` | | Update employer address | `PUT` | `/organizations/{tenant}/people/{personId}/employerAddress` | | Update place of birth | `PUT` | `/organizations/{tenant}/people/{personId}/placeOfBirth` | | Check address | `GET` | `/organizations/{tenant}/addresses/check` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/account_Person-Resource) --- description: >- Transaction and velocity limits per sender trust level — Tier 1 (basic), Tier 2 (verified), and Tier 3 (fully KYC'd) thresholds for daily and monthly transfers. --- # Trust Level Limits The Trust Level (Compliance Level) system controls how much a sender is allowed to transact. Higher levels require more KYC data and documents but unlock higher transaction limits across three rolling time windows: **24 hours**, **30 days**, and **180 days**. *** ### Checking Compliance Level **Endpoint:** `GET /organizations/{tenant}/participants/{participantId}/complianceLevels`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/participants/$SENDER_ID/complianceLevels \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` This endpoint returns: * The participant's **current compliance level** and its limits * The **next available level** and the fields required to upgrade *** ### Sample Response A participant at `LEVEL_0` (cannot transact yet) with upgrade path to `LEVEL_1`: ```json { "currentLevel": { "level": "LEVEL_0", "limits": { "oneDayLimit": { "amount": "0.00", "currency": "USD" }, "thirtyDaysLimit": { "amount": "0.00", "currency": "USD" }, "oneHundredAndEightyDaysLimit": { "amount": "0.00", "currency": "USD" } } }, "nextLevel": { "level": "LEVEL_1", "limits": { "oneDayLimit": { "amount": "2999.00", "currency": "USD" }, "thirtyDaysLimit": { "amount": "6000.00", "currency": "USD" }, "oneHundredAndEightyDaysLimit": { "amount": "9999.00", "currency": "USD" } }, "requiredFields": [ "firstName", "lastName", "phoneNumber", "residentialAddress" ] } } ``` *** ### Compliance Level Overview | Level | Typical Required Fields | Description | | ----- | ----------------------- | ----------- | | `LEVEL_0` | (none) | Default level. **Cannot transact.** Participant exists but has not provided minimum KYC data. | | `LEVEL_1` | firstName, lastName, address, phoneNumber | Basic KYC. Enables standard transaction volumes. | | `LEVEL_2` | SSN/ITIN, occupation, document ID upload | Enhanced KYC. Unlocks higher limits. | | `LEVEL_3` | Proof of source of funds | Full KYC. Maximum transaction limits. | > ⚠️ **These levels are customizable per tenant.** Your specific required fields and limits are defined during the Inyo onboarding process and may differ from the defaults shown above. *** ### Checking Transaction Limits & Usage To see how much of their limit a sender has already used: **Endpoint:** `GET /organizations/{tenant}/fx/participants/{participantId}/limits`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/participants/$SENDER_ID/limits \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` **Sample Response:** ```json { "oneDayLimit": { "limit": { "amount": "2999.00", "currency": "USD" }, "used": { "amount": "500.00", "currency": "USD" }, "available": { "amount": "2499.00", "currency": "USD" } }, "thirtyDaysLimit": { "limit": { "amount": "6000.00", "currency": "USD" }, "used": { "amount": "2959.99", "currency": "USD" }, "available": { "amount": "3040.01", "currency": "USD" } }, "oneHundredAndEightyDaysLimit": { "limit": { "amount": "9999.00", "currency": "USD" }, "used": { "amount": "5000.00", "currency": "USD" }, "available": { "amount": "4999.00", "currency": "USD" } }, "complianceLevel": { "level": "LEVEL_1", "requiredFields": ["birthDate", "lastName", "name", "occupation", "phoneNumber", "residentialAddress"], "perTransactionFields": [] } } ``` **Key fields:** * `limit` — Maximum amount for this time window * `used` — Amount already consumed in the rolling window * `available` — Remaining capacity (`limit - used`) * `complianceLevel.requiredFields` — Fields needed for the *next* level upgrade *** ### Upgrading a Sender's Level To move a sender from one level to the next: 1. **Check current level** — `GET /participants/{id}/complianceLevels` 2. **Identify missing fields** — Look at `nextLevel.requiredFields` 3. **Collect the data** — Update the sender's profile with the missing fields: * Use `PATCH /people/{personId}` for profile fields (SSN, occupation, etc.) * Use `PUT /people/{personId}/address` for address updates * Use document upload endpoints for ID documents and source of funds 4. **Verify the upgrade** — Call the compliance levels endpoint again to confirm the level increased ``` LEVEL_0 LEVEL_1 LEVEL_2 LEVEL_3 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Cannot │ Add │ Standard │ Add │ Enhanced │ Add │ Maximum │ │ transact │──────▶│ limits │──────▶│ limits │──────▶│ limits │ │ │ name, │ │ SSN, │ │ proof │ │ │ $0 / $0 / $0│ addr, │ $X/$X/$X │ docs, │ $X/$X/$X │ of │ $X/$X/$X │ └──────────────┘ phone └──────────────┘ occup └──────────────┘ funds └──────────────┘ ``` *** ### Use Cases * **Progressive onboarding** — Start with minimal data (Level 1) and prompt for more only when the user needs higher limits. * **Limit warnings** — Show users their remaining capacity before they initiate a transaction. * **Upgrade prompts** — When a transaction would exceed the current limit, show exactly which fields are missing and guide the user to complete them. * **Compliance dashboard** — Display a visual progress bar showing the user's level and what's needed for the next tier. *** ### Pending Verifications To check what the compliance team is still reviewing for a participant: **Endpoint:** `GET /organizations/{tenant}/participants/{participantId}/pendingVerifications`\ **Authentication:** Tenant-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/participants/$SENDER_ID/pendingVerifications \ --header "x-api-key: $API_KEY" ``` This is useful for showing users that their documents are under review and their level upgrade is in progress. [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/account_Participant-Resource) --- description: >- Upload KYC identity documents (government ID, proof of address) to elevate a sender's trust level and unlock higher transaction limits. --- # Uploading Documents To advance a sender to higher compliance levels (Level 2 and above), you may need to upload identity documents and proof of source of funds. These documents are reviewed by the Inyo compliance team. *** ### Document Types #### Identity Documents (Document ID) Government-issued identification uploaded for KYC verification. **Endpoint:** `POST /organizations/{tenant}/people/{personId}/documents/documentId/{subtype}/upload`\ **Authentication:** Tenant-level (`x-api-key`) **Supported Subtypes:** | Subtype | Description | | ------- | ----------- | | `FRONT` | Front side of the document | | `BACK` | Back side of the document | **Accepted document types** (configured per tenant): * Driver's License * Passport * National ID Card * State ID #### Source of Funds Proof of the sender's source of funds, required for higher compliance tiers. **Endpoint:** `POST /organizations/{tenant}/people/{personId}/documents/sourceOfFunds/{subtype}/upload`\ **Authentication:** Tenant-level (`x-api-key`) **Supported Subtypes:** | Subtype | Description | | ------- | ----------- | | `FRONT` | Primary document (e.g., bank statement, pay stub) | *** ### Upload Request Documents are uploaded as **multipart/form-data** with the file attached. ```bash # Upload front of driver's license curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people/$PERSON_ID/documents/documentId/FRONT/upload \ --header "x-api-key: $API_KEY" \ --form "file=@/path/to/drivers-license-front.jpg" ``` ```bash # Upload back of driver's license curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people/$PERSON_ID/documents/documentId/BACK/upload \ --header "x-api-key: $API_KEY" \ --form "file=@/path/to/drivers-license-back.jpg" ``` ```bash # Upload source of funds curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people/$PERSON_ID/documents/sourceOfFunds/FRONT/upload \ --header "x-api-key: $API_KEY" \ --form "file=@/path/to/bank-statement.pdf" ``` *** ### Document Verification Status After uploading, documents are queued for review by the compliance team. You can track the verification status: #### Get Current Verification Status **Endpoint:** `GET /organizations/{tenant}/documents/{documentId}/status`\ **Authentication:** Tenant-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/documents/$DOCUMENT_ID/status \ --header "x-api-key: $API_KEY" ``` #### Get Verification Status History **Endpoint:** `GET /organizations/{tenant}/documents/{documentId}/status/history`\ **Authentication:** Tenant-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/documents/$DOCUMENT_ID/status/history \ --header "x-api-key: $API_KEY" ``` #### List Person Documents by Status **Endpoint:** `GET /organizations/{tenant}/people/{personId}/documents`\ **Authentication:** Tenant-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people/$PERSON_ID/documents \ --header "x-api-key: $API_KEY" ``` *** ### Verification Statuses | Status | Description | | ------ | ----------- | | `Pending` | Document uploaded, awaiting review | | `Verified` | Document accepted — compliance level may upgrade | | `NotVerified` | Document rejected — see reason and re-upload | | `NotReadable` | Document image is unclear — request a better scan | | `TemplateIdentified` | Document type recognized but verification in progress | *** ### Webhook Notifications Register for the `documentUpdatedEvents` webhook to receive real-time notifications when a document's verification status changes. See [Webhooks](../webhooks.md) for setup instructions. *** ### Best Practices * **Upload high-quality images** — blurry or cropped images will be rejected as `NotReadable`. * **Upload both sides** when required — for documents like driver's licenses, upload both `FRONT` and `BACK`. * **Check compliance level after verification** — once a document is `Verified`, call `GET /participants/{id}/complianceLevels` to see if the sender has been upgraded. * **Handle rejections gracefully** — prompt the user to re-upload with clear instructions about what went wrong. * **Use webhooks** instead of polling — register for `documentUpdatedEvents` to be notified immediately when a document is reviewed. *** ### Test Data For sandbox testing, use the test data provided in [Test Data](test-data.md). Certain name/address combinations trigger specific compliance behaviors (approval, hold, rejection). [Interactive API Documentation — Document ID](https://dev-api.inyoglobal.com/sandbox/#tag/account_Person-Documents-Resource/paths/~1organizations~1%7Btenant%7D~1people~1%7BpersonId%7D~1documents~1documentId~1%7Bsubtype%7D~1upload/post)\ [Interactive API Documentation — Source of Funds](https://dev-api.inyoglobal.com/sandbox/#tag/account_Person-Documents-Resource/paths/~1organizations~1%7Btenant%7D~1people~1%7BpersonId%7D~1documents~1sourceOfFunds~1%7Bsubtype%7D~1upload/post) --- description: >- Sandbox test identities and data for remittance sender testing — pre-configured sender profiles and scenarios for integration validation. --- # Test data The following data can be used to test different compliance and transaction validation scenarios: | Parameter | Value | Result | Compliance Status | Payout Status | | -------------------- | ---------------- | -------------------------------------------------------- | ----------------- | ------------- | | address.zipcode | 99999 | Transaction will be REJECTED | Rejected | Cancelled | | phoneNumber | +14155550000 | Transaction will be REJECTED | Rejected | Cancelled | | firstName + lastName | BLOCK LIST MATCH | Transaction will be REJECTED | Rejected | Cancelled | | firstName + lastName | OFAC MATCH | Transaction will be put ON HOLD for further verification | Hold | Pending | --- description: >- Link a funding source (bank account or wallet) to a sender entity to cover remittance disbursements. --- # Sender Funding Account A Funding Account represents the source of funds used by the sender to pay for a transaction. Inyo supports multiple payment methods, all abstracted through a unified ledger system for compliance, reconciliation, and settlement tracking. *** ### Funding Account Statuses Every funding account has a `status` field that reflects its current verification state. Your application should handle all four statuses: | Status | Description | Can Be Used for Transactions? | | ------ | ----------- | ----------------------------- | | `Pending` | Account has been created but verification is not yet complete (e.g., waiting for Plaid account verification or initial processing). | No | | `ActionRequired` | Additional action is needed from the user — typically a 3D Secure (3DS) authentication challenge for card payments. See [Handling 3D Secure](../payments-gateway/apis/payment/pulling-funds/cards/authorizing/handling-3d-secure.md). | No | | `Verified` | Account has been fully verified and is ready to fund transactions. | Yes | | `Rejected` | Verification failed — the card was declined, 3DS authentication failed, or the bank account could not be verified. The sender must add a new funding account. | No | > **Note for API consumers:** The `status` field in funding account responses is returned as a string. The four values above are the complete set of possible statuses. #### Status Lifecycle ``` ┌──────────────┐ │ Pending │ └──────┬───────┘ │ ┌───────────┼───────────┐ ▼ │ ▼ ┌───────────────┐ │ ┌───────────────┐ │ActionRequired │ │ │ Verified │ └───────┬───────┘ │ └───────────────┘ │ │ ┌───────┼───────┐ │ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ Verified │ │ Rejected │ └───────────────┘ └───────────────┘ ``` - **Card (CARD):** Typically transitions `Pending` → `ActionRequired` (3DS challenge) → `Verified` or `Rejected`. Some low-risk cards may go directly to `Verified`. - **ACH:** Typically transitions `Pending` → `Verified` or `Rejected` after Plaid account verification completes. - **Wallet (WALLET):** Usually transitions directly to `Verified`. > **Always check the status** before using a funding account in a transaction. Attempting to use a `Pending`, `ActionRequired`, or `Rejected` funding account will result in an error. > **Handling `Rejected`:** If a funding account is rejected, it cannot be retried. The sender must create a new funding account with corrected or alternative payment details. *** ### Supported Payment Methods | Method | Type Value | Description | Speed | | ------ | ---------- | ----------- | ----- | | Debit Card | `CARD` | Tokenized card payment via AFT (Account Funding Transaction) | Instant | | ACH | `ACH` | US bank transfer that uses Plaid verification | Varies according to destination bank | | Wallet | `WALLET` | Internal wallet / P2P balance | Instant | *** ### Creating a Funding Account **Endpoint:** `POST /organizations/{tenant}/payout/participants/{participantId}/fundingAccounts`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) #### Common Request Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `externalId` | string | Yes | Your internal reference for this funding source | | `asset` | string | Yes | Currency code — must be `USD` | | `nickname` | string | No | Display name (e.g., "My Visa Debit") | | `paymentMethod` | object | Yes | Payment method details (varies by type — see options below) | *** #### Option A: Debit Card Card payments require a **tokenized card number**. Raw card details are never sent to the Inyo Core API — they are first sent to the tokenization service (see [Tokenizing Cards](../payments-gateway/apis/tokenizing-cards.md)) to obtain a secure token. ##### Card Payment Method Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `type` | string | Yes | Must be `CARD` | | `ipAddress` | string | Yes | IP address of the cardholder's device | | `token` | string | Yes | Tokenized card number from the tokenization service | | `schemeId` | string | Yes | Card network identifier (e.g., `VISA`, `MASTERCARD`) | | `bin` | string | Yes | First 6 digits of the card number (Bank Identification Number) | | `lastFourDigits` | string | Yes | Last 4 digits of the card number | | `billingAddress` | object | Yes | Cardholder billing address | | `billingAddress.countryCode` | string | Yes | Two-letter ISO country code (e.g., `US`) | | `billingAddress.stateCode` | string | No | State/province code (e.g., `CA`) | | `billingAddress.city` | string | No | City name | | `billingAddress.line1` | string | No | Street address line 1 | | `billingAddress.line2` | string | No | Street address line 2 | | `billingAddress.zipcode` | string | No | Postal/ZIP code | ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "000000001", "asset": "USD", "nickname": "My USD card", "paymentMethod": { "type": "CARD", "ipAddress": "127.0.0.1", "token": "d5a41e06-e3dc-448f-b2bb-ff8dc0ed1b93", "bin": "541333", "schemeId": "MASTERCARD", "lastFourDigits": "3303", "billingAddress": { "countryCode": "US", "stateCode": "CA", "city": "LAKEWOOD", "line1": "4429 CANDLEWOOD ST", "line2": "Some line 2 address", "zipcode": "90712" } } }' ``` **Example Card Response:** ```json { "id": "3e1463df-9414-433b-91e9-b7b21d06e095", "createdAt": "2025-08-25T16:03:42", "asset": "USD", "nickname": "", "token": "9a7574a9-c93b-458e-ab59-9c34697d2ddc", "bin": "520000", "schemeId": "MASTERCARD", "lastFourDigits": "2235", "billingAddressId": "cf92a897-cbda-4350-8930-cb335da08ac6", "type": "CARD", "status": "Verified", "statusMessage": "Test card USD funding account", "externalId": null, "tenantId": "inyo", "representative": { "id": "57104187-3c70-4d43-867d-c47b4a6961eb", "type": "PARTICIPANT" } } ``` **3D Secure (3DS) Challenge:** Some cards require additional authentication. If the response returns `status: "ActionRequired"`: 1. Save the funding account locally with a `Pending` status 2. Display the provided `redirectAcsUrl` in an iframe for the user to complete the challenge 3. Listen for a `postMessage` event from the iframe confirming authorization 4. Call `GET /organizations/{tenant}/payout/fundingAccounts/{fundingAccountId}` to check the updated status 5. If `Verified` — the card is ready to use for transactions 6. If `Rejected` — the 3DS challenge failed or the card was declined; prompt the sender to add a different card For full 3DS implementation details, see [Handling 3D Secure](../payments-gateway/apis/payment/pulling-funds/cards/authorizing/handling-3d-secure.md). *** #### Option B: ACH (US Bank Account) ACH funding accounts use **Plaid** for secure bank account verification. You never send raw routing or account numbers to the Inyo API — instead, the user connects their bank account through Plaid Link, and you pass the resulting tokens to Inyo. ##### Step 1: Create a Plaid Link Token Before the user can connect their bank account, your backend must request a Plaid Link token from Inyo. **Endpoint:** `POST /organizations/{tenant}/payout/participants/{participantId}/fundingAccounts/linkTokens`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `provider` | string | No | Account verification provider — defaults to `PLAID` | ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts/linkTokens \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "provider": "PLAID" }' ``` **Response:** ```json { "token": "link-sandbox-c2ad1be1-20fe-4944-a8f7-2d64b89412f1", "expireAt": "2025-11-26T13:46:42Z", "requestId": "Jc6pgl5mY3tse0P" } ``` ##### Step 2: Launch Plaid Link on the Client Use the `token` from Step 1 to initialize Plaid Link in your client application. The user will select their bank and authenticate. On success, Plaid Link returns a `public_token` and an `account_id` — these are the values you need for Step 3. For Plaid Link integration details, refer to the [Plaid Link documentation](https://plaid.com/docs/link/). ##### Step 3: Register the ACH Funding Account Pass the Plaid `account_id` as `accountCheckId` and the Plaid `public_token` as `accountCheckToken` to create the funding account. ##### ACH Payment Method Fields | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `type` | string | Yes | Must be `ACH` | | `countryCode` | string | Yes | Must be `US` | | `accountCheckId` | string | Yes | Plaid `account_id` returned from Plaid Link | | `accountCheckToken` | string | Yes | Plaid `public_token` returned from Plaid Link | ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "000000002", "asset": "USD", "nickname": "My USD ACH funding account", "paymentMethod": { "type": "ACH", "countryCode": "US", "accountCheckId": "zXn435xnnJsZBjq3WjA5T7ZeRmdZyotJMNjbA", "accountCheckToken": "public-sandbox-0446ca14-0234-4475-afe2-9daf1871755d" } }' ``` **Example ACH Response:** ```json { "id": "94800c36-ce22-42ed-a45c-00dde5bd1f9f", "createdAt": "2025-11-26T13:46:42", "asset": "USD", "nickname": "My USD ACH funding account", "countryCode": "US", "bankName": "Tartan Bank", "routingNumber": "*****1533", "accountNumber": "************1111", "type": "ACH", "status": "Verified", "statusMessage": "Test ACH USD funding account", "externalId": "000000002", "tenantId": "inyo", "representative": { "id": "57104187-3c70-4d43-867d-c47b4a6961eb", "type": "PARTICIPANT" }, "accountTokenId": "access-sandbox-96027a8a-c796-4298-b867-3b82726faf16" } ``` > Note that routing and account numbers are returned **masked** in the response. The full values are never exposed through the API — Plaid handles the secure bank account connection. > **Save the returned `id`** — this is the `fundingAccountId` required when creating a transaction. *** ### Listing Funding Accounts **Endpoint:** `GET /organizations/{tenant}/payout/participants/{participantId}/fundingAccounts`\ **Authentication:** Agent-level Supports sorting via the `sort` query parameter. Each item should be in the format `field,order` where `field` is one of `createdAt` and `order` is either `ASC` or `DESC`. ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$SENDER_ID/fundingAccounts \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` Use this to show the sender their saved payment methods and let them select one for a transaction. *** ### Getting a Funding Account **Endpoint:** `GET /organizations/{tenant}/payout/fundingAccounts/{fundingAccountId}`\ **Authentication:** Agent-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/fundingAccounts/$FUNDING_ACCOUNT_ID \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` Use this to check a funding account's current status. Common use cases: - After a 3DS challenge to confirm `Verified` or detect `Rejected` - Before creating a transaction to ensure the funding account is still `Verified` - Polling after ACH account creation to check if verification has completed > **Tip:** Implement a polling strategy or webhook listener to detect status transitions from `Pending` → `Verified`/`Rejected`, rather than relying on the user to manually refresh. *** ### How Funding Works in the Transaction Lifecycle ``` 1. Sender selects payment method → Use existing or create new funding account 2. Transaction submitted → Funds are debit-locked from the funding source 3. Compliance approved → Funds are collected (captured) 4. Payout processed → Funds settled to recipient via payout network 5. Transaction recorded → Ledger entry created for reconciliation ``` *** ### All Endpoints | Operation | Method | Endpoint | | --------- | ------ | -------- | | Create Plaid link token | `POST` | `/organizations/{tenant}/payout/participants/{participantId}/fundingAccounts/linkTokens` | | Create funding account | `POST` | `/organizations/{tenant}/payout/participants/{participantId}/fundingAccounts` | | List funding accounts | `GET` | `/organizations/{tenant}/payout/participants/{participantId}/fundingAccounts` | | Get funding account | `GET` | `/organizations/{tenant}/payout/fundingAccounts/{fundingAccountId}` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Funding-Account-Resource) --- description: >- Create and manage recipient profiles for international payout destinations — includes required fields, country-specific schemas, and update/delete operations. --- # Recipient A **Recipient** (beneficiary) is the person or company who receives the funds. Recipients are created using the same participant endpoints as senders — the system distinguishes roles based on how the `participantId` is used in a transaction (`recipientId` field). *** ### Schema-Driven Recipient Creation Recipient data requirements **vary by destination country**. Before collecting user data, always fetch the recipient schema: ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/recipients/schema/co \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` This returns a JSON Schema (draft-07) defining exactly which fields are required for that country. See [Recipient Schema](data-population/recipient-schema.md) for details. *** ### Creating a Recipient **Endpoint:** `POST /organizations/{tenant}/people`\ **Authentication:** Tenant-level (`x-api-key`) #### Example: Brazilian Recipient ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "firstName": "Maria", "lastName": "Silva", "phoneNumber": "+5511999998888", "documents": [ { "type": "CPF", "document": "12345678901", "countryCode": "BR" } ] }' ``` #### Example: Colombian Recipient ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/people \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "firstName": "Carlos", "lastName": "Gutierrez", "phoneNumber": "+573001234567", "address": { "countryCode": "CO", "city": "Bogotá", "line1": "Calle 100 #15-20" }, "documents": [ { "type": "CC", "document": "1234567890", "countryCode": "CO" } ] }' ``` > ⚠️ **Save the returned `id`** — this is the `recipientId` used in the transaction. *** ### Country-Specific Document Types | Country | Document Type | Code | Format | | ------- | ------------- | ---- | ------ | | Brazil | CPF (Cadastro de Pessoas Físicas) | `CPF` | 11 digits | | Colombia | Cédula de Ciudadanía | `CC` | 8–10 digits | | Mexico | CURP / INE | Varies | Per schema | | Peru | DNI (Documento Nacional de Identidad) | `DNI` | 8 digits | | India | PAN / Aadhaar | Varies | Per schema | | USA | SSN / ITIN | `SSN`, `ITIN` | 9 digits | > 💡 Always consult the recipient schema for the authoritative list of accepted document types per country. *** ### Reusing Participants Participants are reusable entities: * A person created as a **recipient** can later be used as a **sender** (if they meet compliance requirements) * A person can act as **both sender and recipient** in the same transaction (self-send) by passing the same `participantId` for both `senderId` and `recipientId` * A participant only needs to be created **once** and can be referenced in unlimited future transactions *** ### After Creating the Recipient Once you have the `recipientId`, the next step is to link a bank account: 1. **Fetch the account schema** — `GET /payout/recipientAccounts/schema/{countryCode}` 2. **Fetch the bank list** (if required) — `GET /payout/{countryCode}/banks` 3. **Create the recipient account** — `POST /payout/participants/{recipientId}/recipientAccounts/gateway` See [Recipient Account](recipient-account.md) for full details. *** ### Best Practices * **Always use the schema endpoint** to determine required fields — don't hardcode field requirements per country. * **Dynamically render forms** based on the schema response to automatically support new countries. * **Collect only required fields** — submitting unnecessary data may trigger additional compliance checks. * **Validate input client-side** using the schema's `pattern`, `minLength`, and `enum` constraints before submitting. * **Handle the `address` requirement** — some countries require a recipient address, others don't. The schema is your source of truth. [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/account_Person-Resource) --- description: >- Add and manage bank accounts or wallet identifiers for a recipient — supports bank transfers, mobile wallets, PIX, and card payouts. --- # Recipient Account A Recipient Account represents the bank account or payout destination where funds will be delivered. Before creating a recipient account, you must first [create the recipient](recipient.md) as a participant and [fetch the account schema](data-population/recipient-account-schema.md) for the destination country. *** ### Schema-Driven Creation Recipient account requirements **vary by country**. Always fetch the account schema before building your form or API call: ```bash # Fetch account schema for Colombia curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/recipientAccounts/schema/co \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` The schema returns a JSON Schema (draft-07) object that defines required fields, allowed values, and validation rules. Common fields include: | Field | Description | Examples | | ----- | ----------- | -------- | | `asset` | Currency code (ISO 4217) | `BRL`, `MXN`, `COP`, `PEN` | | `payoutMethod.type` | Payout mechanism | `BANK_DEPOSIT` | | `payoutMethod.countryCode` | Destination country | `BR`, `MX`, `CO` | | `payoutMethod.bankCode` | Bank identifier | Varies by country (use [Banks endpoint](data-population/banks-in-a-country.md)) | | `payoutMethod.routingNumber` | Routing/SWIFT/BIC code | Country-specific | | `payoutMethod.accountNumber` | Account number | CLABE (MX), IBAN, or local account number | | `payoutMethod.accountType` | Account type | `CHECKING`, `SAVINGS` | > 💡 **Tip:** For countries that require a `bankCode`, use the [Banks in a Country](data-population/banks-in-a-country.md) endpoint to populate a dropdown in your UI. *** ### Creating a Recipient Account **Endpoint:** `POST /organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/gateway`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) #### Request Body | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `externalId` | string | No | Your internal identifier for this account | | `asset` | string | Yes | Currency code (ISO 4217) for the recipient's account | | `payoutMethod` | object | Yes | Bank account details (country-specific, see schema) | #### Example: Colombia (Bank Deposit) ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/gateway \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "acct-co-001", "asset": "COP", "payoutMethod": { "type": "BANK_DEPOSIT", "countryCode": "CO", "bankCode": "1001", "routingNumber": "1234", "accountNumber": "9876543210", "accountType": "SAVINGS" } }' ``` #### Example: Mexico (CLABE) ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/gateway \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "acct-mx-001", "asset": "MXN", "payoutMethod": { "type": "BANK_DEPOSIT", "countryCode": "MX", "bankCode": "002", "routingNumber": "002", "accountNumber": "012345678901234567", "accountType": "CHECKING" } }' ``` #### Example: Peru (Bank Deposit) ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/gateway \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "externalId": "acct-pe-001", "asset": "PEN", "payoutMethod": { "type": "BANK_DEPOSIT", "countryCode": "PE", "bankCode": "BINPPEPL", "routingNumber": "1234", "accountNumber": "1345", "accountType": "CHECKING" } }' ``` > ⚠️ **Save the returned `id`** — this is the `recipientAccountId` required when creating a transaction. *** ### Getting a Recipient Account **Endpoint:** `GET /organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/{recipientAccountId}`\ **Authentication:** Agent-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/$ACCOUNT_ID \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` *** ### Deleting a Recipient Account **Endpoint:** `DELETE /organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/{recipientAccountId}`\ **Authentication:** Agent-level ```bash curl --request DELETE \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/payout/participants/$RECIPIENT_ID/recipientAccounts/$ACCOUNT_ID \ --header "x-api-key: $API_KEY" \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` *** ### Country-Specific Requirements | Country | Currency | Key Field | Document Type | Notes | | ------- | -------- | --------- | ------------- | ----- | | Brazil (BR) | BRL | `accountNumber` | CPF | PIX keys may be supported depending on your corridor config | | Mexico (MX) | MXN | `accountNumber` (CLABE, 18 digits) | CURP / INE | CLABE is the standard interbank identifier | | Colombia (CO) | COP | `bankCode` + `accountNumber` | CC (Cédula) | Use the Banks endpoint for valid `bankCode` values | | Peru (PE) | PEN | `bankCode` + `accountNumber` | DNI | Bank codes use SWIFT/BIC format | | India (IN) | INR | `routingNumber` (IFSC) | PAN / Aadhaar | IFSC code is mandatory for Indian bank transfers | | Philippines (PH) | PHP | `bankCode` + `accountNumber` | — | — | > 💡 Always use the schema endpoint as the source of truth — the table above is for guidance only. *** ### Integration Workflow ``` 1. Fetch account schema → GET /payout/recipientAccounts/schema/{countryCode} 2. Fetch bank list → GET /payout/{countryCode}/banks (if bankCode is required) 3. Collect user input → Build form from schema 4. Create account → POST /payout/participants/{id}/recipientAccounts/gateway 5. Save account ID → Use in POST /fx/transactions ``` *** ### All Endpoints | Operation | Method | Endpoint | | --------- | ------ | -------- | | Create recipient account | `POST` | `/organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/gateway` | | Get recipient account | `GET` | `/organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/{recipientAccountId}` | | Delete recipient account | `DELETE` | `/organizations/{tenant}/payout/participants/{participantId}/recipientAccounts/{recipientAccountId}` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/payout_Recipient-Account-Resource) --- description: >- Create, track, and manage remittance transactions — execute cross-border transfers, monitor status transitions, and handle transaction lifecycle events. --- # Transaction The Transaction endpoint is the culmination of the remittance flow. It links together the sender, recipient, funding source, recipient account, and quote to execute a cross-border payment. *** ### Creating a Transaction **Endpoint:** `POST /organizations/{tenant}/fx/transactions`\ **Authentication:** Agent-level (`x-api-key` + `x-agent-id` + `x-agent-api-key`) #### Request Body | Field | Type | Required | Description | | -------------------------- | ------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `senderId` | string (UUID) | Yes | The participant ID of the sender (from `POST /people`) | | `recipientId` | string (UUID) | Yes | The participant ID of the recipient (from `POST /people`) | | `fundingAccountId` | string (UUID) | Yes | The sender's funding source (from `POST /fundingAccounts`) | | `recipientAccountId` | string (UUID) | Yes | The recipient's bank account (from `POST /recipientAccounts/gateway`) | | `quoteId` | string (UUID) | Yes | The locked FX quote (from `POST /payout/quotes`) | | `externalId` | string (UUID) | No | Your internal reference ID for idempotency and reconciliation | | `additionalData` | object | Yes | Country-specific data and device fingerprint (see below). Pass `{}` if no additional data is required for the corridor. | | `additionalData.fingerprint` | string | Yes | The `requestId` returned by the device fingerprint library | | `additionalData.visitor` | string | Yes | The `visitorId` returned by the device fingerprint library | | `deviceData` | object | No | Device fingerprinting data for fraud prevention | | `deviceData.userIpAddress` | string | Recommended | The end user's IP address | #### Device Fingerprint (Required) Every transaction requires a device fingerprint for fraud prevention. You must integrate a device fingerprint library into your client application. Inyo will provide the API keys for the fingerprint service during onboarding. Before submitting a transaction, call the fingerprint library on the client side to obtain a `requestId` and `visitorId`. Pass these values in the `additionalData` object: - `additionalData.fingerprint` — the `requestId` from the fingerprint library - `additionalData.visitor` — the `visitorId` from the fingerprint library > These fields are required on every transaction. Requests without valid fingerprint data will be rejected. #### Sample Request ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions \ --header 'Content-Type: application/json' \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" \ --data '{ "senderId": "422a55b2-78c3-4509-8654-16e7375d3e40", "recipientId": "4b9dcb4e-2e2a-4e96-b2a9-fcd868b2f4ed", "fundingAccountId": "3fe5f23f-65d3-41b6-b34c-33db3c4d8ef9", "recipientAccountId": "67203d69-dc48-41d4-b2ac-52682d39b032", "quoteId": "30bf05d2-b416-4e71-b8e8-1ef158a2b414", "additionalData": { "fingerprint": "1727364523875.mchLfB", "visitor": "jkS9xp4FHlqOSfGR" }, "deviceData": { "userIpAddress": "200.123.131.112" } }' ``` #### Sample Response (HTTP 202) ```json { "id": "9035ee18-052b-4176-a940-ccfe599a1829", "sender": { "id": "ce06773d-6472-4c93-bdf5-8eb68ab01c7f", "type": "PERSON", "name": "Rob Dalas", "phoneNumber": "+1 123 456435" }, "recipient": { "id": "156675d5-1d1b-4b4d-997e-5c15ae129b82", "type": "PERSON", "name": "Bob Danilo", "phoneNumber": "+55 71 91234-5678" }, "tenantId": "master", "externalId": null, "agentId": "41f55211-1eea-4747-9a03-68e99f07ede5", "createdAt": "2025-05-29T17:26:27.264312", "anchorCurrencyAmount": { "amount": "102.53", "currency": "USD" }, "complianceStatus": "Approved", "payoutStatus": "Pending", "status": { "id": "0097c1fb-56ac-4314-afa1-eaf00e712d5b", "transactionId": "e58796a9-e809-4503-8ce3-2c96c0a0e170", "complianceStatus": "Approved", "payoutStatus": "Pending", "messages": [ { "id": "7122d9ab-2e60-41a4-94aa-e4231b1ca4f7", "message": "STANDARD - PAYMENT_RECEIVED", "createdBy": "system", "createdAt": "2025-11-21T11:48:10.268817", "type": "SystemUpdate" } ], "createdAt": "2025-11-21T14:48:05.891186" }, "quoteId": "af2d55d8-1d40-426a-8d4a-7454ccdcbe8a", "fundingAccountId": "61bc14a1-b3a2-40b4-b395-0496e4f554a1", "recipientAccountId": "4fbc57a6-f673-44ba-88aa-c6e71eb9181b", "exchangeRate": "5.571062480000000", "totalAmount": { "amount": "102.53", "currency": "USD" }, "receivingAmount": { "amount": "571.20", "currency": "BRL" }, "fee": { "amount": "1.54", "currency": "USD" }, "conversionAmount": { "amount": "102.53", "currency": "USD" }, "originExchangeRate": { "fromCurrency": "USD", "toCurrency": "USD", "rate": "1.000000000000000" }, "destinationExchangeRate": { "fromCurrency": "USD", "toCurrency": "BRL", "rate": "5.571062480000000" }, "type": "FX", "senderId": "ce06773d-6472-4c93-bdf5-8eb68ab01c7f", "recipientId": "156675d5-1d1b-4b4d-997e-5c15ae129b82" } ``` #### Response Fields | Field | Description | | ------------------------- | ------------------------------------------------------------------ | | `id` | Unique transaction ID | | `complianceStatus` | Current compliance screening result | | `payoutStatus` | Current payout delivery status | | `status.messages` | Audit trail of status transitions with timestamps | | `exchangeRate` | The applied FX rate | | `totalAmount` | Total amount charged to the sender (in source currency) | | `receivingAmount` | Amount to be received by the beneficiary (in destination currency) | | `fee` | Transaction fee in source currency | | `conversionAmount` | Amount that was converted (source currency) | | `originExchangeRate` | Exchange rate from source currency to anchor currency | | `destinationExchangeRate` | Exchange rate from anchor currency to destination currency | | `type` | Transaction type: `FX`, `TOP_UP`, or `BILLPAY` | *** ### Transaction Status Lifecycle Every transaction has two independent status dimensions: #### Compliance Status | Status | Description | | ---------- | ------------------------------------------------------ | | `Approved` | Passed all compliance checks — transaction can proceed | | `Hold` | Flagged for manual review by the compliance team | | `Rejected` | Failed compliance screening — transaction is blocked | #### Payout Status | Status | Description | | ----------- | ------------------------------------------------------------------- | | `Pending` | Transaction accepted, awaiting processing | | `Paid` | Funds successfully delivered to the recipient | | `Cancelled` | Transaction was cancelled (by user request or compliance rejection) | | `Failed` | Transaction was failed (by payment gateway) | | `Refunded` | Transaction was refunded | #### Compliance Status Transitions ``` ┌──────────┐ ┌─────│ │─────┐ │ └────┬─────┘ │ │ │ │ │ │ │ │ ┌────▼─────┐ │ │┌────│ Hold │────┐│ ││ └──────────┘ ││ ││ ││ ┌────▼──────┐ ┌─────▼▼────┐ │ Rejected │ │ Approved │ │(Cancelled)│ └───────────┘ └───────────┘ ``` *** ### Querying Transactions #### Get Transaction by ID ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions/$TRANSACTION_ID \ --header "x-api-key: $API_KEY" ``` #### List Transactions with Filters ```bash curl --request GET \ --url "https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions?page=0&size=20&type=FX&complianceStatus=Approved" \ --header "x-api-key: $API_KEY" ``` **Query Parameters:** | Parameter | Type | Description | | ------------------ | ------- | ----------------------------------------- | | `page` | integer | Page number (0-indexed) | | `size` | integer | Results per page | | `type` | string | Filter by type: `FX`, `TOP_UP`, `BILLPAY` | | `complianceStatus` | string | Filter by compliance status | | `payoutStatus` | string | Filter by payout status | | `senderName` | string | Filter by sender name | | `receiverName` | string | Filter by receiver name | | `from` | string | Start date filter | | `to` | string | End date filter | #### Get Transaction Status History Returns the full audit trail of status changes for a transaction. ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/status \ --header "x-api-key: $API_KEY" ``` **Sample Response:** ```json [ { "id": "2f6b3f12-a63f-43f7-9bb4-4e98dfc75fe5", "complianceStatus": "Approved", "payoutStatus": "Pending", "transactionId": "46b4c393-3768-4ba4-b6d2-a6398e17dd78", "tenantId": "master", "externalId": "GMT000648512199", "createdAt": "2024-01-03T12:54:09.455", "messages": [ { "id": "0fbbddb8-283d-466d-b2e6-42dcbb3e67de", "message": "STANDARD - PAYMENT_RECEIVED", "createdBy": "system", "createdAt": "2025-11-19T13:29:52.221882", "type": "SystemUpdate" } ] } ] ``` *** ### Cancelling a Transaction Cancel a transaction that has not yet been paid out. Cancellation is allowed when the payout status is `Pending`. **Endpoint:** `PUT /organizations/{tenant}/fx/transactions/{transactionId}/status/cancel`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request PUT \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/status/cancel \ --header "x-api-key: $API_KEY" ``` **Responses:** | Status | Description | | ------ | --------------------------------------------------------- | | `204` | Transaction successfully cancelled | | `400` | Cannot cancel — transaction is in a non-cancellable state | | `404` | Transaction not found | > ⚠️ Once a transaction reaches `Paid` status, it cannot be cancelled — use the refund endpoint instead. *** ### Refunding a Transaction Refund a transaction that has already been paid. **Endpoint:** `PUT /organizations/{tenant}/fx/transactions/{transactionId}/status/refund`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request PUT \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/status/refund \ --header "x-api-key: $API_KEY" ``` **Responses:** | Status | Description | | ------ | ---------------------------------------------------------- | | `204` | Refund initiated | | `400` | Cannot refund — transaction is not in `Paid` payout status | | `404` | Transaction not found | *** ### Updating Transaction Metadata Attach arbitrary key-value data to a transaction for your internal tracking. **Endpoint:** `PUT /organizations/{tenant}/fx/transactions/{transactionId}/metadata`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request PUT \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/metadata \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "internalRef": "INV-2025-001", "department": "treasury" }' ``` *** ### Transaction Limits Before executing a transaction, verify the sender has not exceeded their limits. **Endpoint:** `GET /organizations/{tenant}/fx/participants/{participantId}/limits`\ **Authentication:** Agent-level ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/fx/participants/$SENDER_ID/limits \ --header "x-agent-id: $AGENT_ID" \ --header "x-agent-api-key: $AGENT_KEY" ``` **Sample Response:** ```json { "oneDayLimit": { "limit": { "amount": "2999.00", "currency": "USD" }, "used": { "amount": "0.00", "currency": "USD" }, "available": { "amount": "2999.00", "currency": "USD" } }, "thirtyDaysLimit": { "limit": { "amount": "6000.00", "currency": "USD" }, "used": { "amount": "2959.99", "currency": "USD" }, "available": { "amount": "3040.01", "currency": "USD" } }, "oneHundredAndEightyDaysLimit": { "limit": { "amount": "9999.00", "currency": "USD" }, "used": { "amount": "5000.00", "currency": "USD" }, "available": { "amount": "4999.00", "currency": "USD" } }, "complianceLevel": { "level": "LEVEL_1", "requiredFields": ["birthDate", "lastName", "name", "occupation", "phoneNumber", "residentialAddress"] } } ``` *** ### Receipts (Regulatory Requirement) As an agent of a licensed Money Service Business, you are **legally required** to issue a receipt to the sender immediately after transaction submission. The transaction response includes a `receipt` object with mandatory regulatory disclosures that **must be displayed verbatim** to the end user. The receipt must include: 1. **Exchange rate** — the locked-in rate from the quote 2. **Fees** — total fees charged to the customer 3. **Total amount** — full amount paid by the sender 4. **Receive amount** — exact amount to be received by the beneficiary 5. **Regulatory disclosures** — dynamic legal text specific to the corridor (e.g., right to refund, cancellation policy) > ⚠️ You **must not** alter the financial data or regulatory text returned by the API. Display it as-is. *** ### Error Responses | HTTP Status | Error | Cause | | ----------- | -------------------- | ------------------------------------------------------------------ | | `400` | Bad request | Missing required fields, invalid data format, or expired quote | | `403` | Forbidden | Agent not approved, or sender doesn't meet compliance requirements | | `422` | Unprocessable entity | Business rule violation (e.g., limit exceeded, invalid corridor) | *** ### Additional Endpoints | Operation | Method | Endpoint | | ---------------------- | ------ | ----------------------------------------------------------------------- | | Create transaction | `POST` | `/organizations/{tenant}/fx/transactions` | | Get transaction | `GET` | `/organizations/{tenant}/fx/transactions/{transactionId}` | | List transactions | `GET` | `/organizations/{tenant}/fx/transactions` | | Get status history | `GET` | `/organizations/{tenant}/fx/transactions/{transactionId}/status` | | Cancel transaction | `PUT` | `/organizations/{tenant}/fx/transactions/{transactionId}/status/cancel` | | Refund transaction | `PUT` | `/organizations/{tenant}/fx/transactions/{transactionId}/status/refund` | | Update metadata | `PUT` | `/organizations/{tenant}/fx/transactions/{transactionId}/metadata` | | Check limits | `GET` | `/organizations/{tenant}/fx/participants/{participantId}/limits` | | Get transaction schema | `GET` | `/organizations/{tenant}/fx/transactions/schema?countryCode={iso2}` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/fx_Transaction-Resource) --- description: >- Webhook notifications for remittance transaction status changes — configure endpoints, verify signatures, and handle retry logic. --- # Webhooks Webhooks allow you to receive real-time notifications when important events occur in the Inyo platform — such as a transaction changing status from `Pending` to `Paid`, or a document verification completing. Instead of polling the API, you register a callback URL and Inyo pushes events to you. *** ### Supported Events | Event | Description | When It Fires | | ------------------------------------ | ----------------------------------------------- | -------------------------------------------------------------- | | `TransactionStatusChanged` | A transaction's payout status has changed | Status transitions (e.g., Pending → Paid, Pending → Cancelled) | | `documentUpdatedEvents` | A document verification status has been updated | After compliance team reviews an uploaded document | | `agentStatusChangedEvent` | An agent's status or configuration has changed | Agent approval, suspension, or configuration updates | | `TransactionComplianceStatusChanged` | A transaction's compliance status has changed | Status transitions (e.g., Hold → Approved, Hold → Rejected) | *** ### Registering a Webhook **Endpoint:** `POST /organizations/{tenant}/webhooks`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request POST \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/webhooks \ --header 'Content-Type: application/json' \ --header "x-api-key: $API_KEY" \ --data '{ "events": [ "transactionStatusChanged", "documentUpdatedEvents", "agentUpdatedEvents" ], "url": "https://your-server.com/api/webhooks/inyo" }' ``` > 💡 You can register for all events or only the ones relevant to your integration. *** ### Listing Registered Webhooks **Endpoint:** `GET /organizations/{tenant}/webhooks`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request GET \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/webhooks \ --header "x-api-key: $API_KEY" ``` *** ### Deleting a Webhook **Endpoint:** `DELETE /organizations/{tenant}/webhooks/{webhookId}`\ **Authentication:** Tenant-level (`x-api-key`) ```bash curl --request DELETE \ --url https://api.sandbox.inyoplatform.com/organizations/$TENANT/webhooks/$WEBHOOK_ID \ --header "x-api-key: $API_KEY" ``` *** ### Webhook Payload When an event occurs, Inyo sends an HTTP `POST` request to your registered URL with a JSON payload. Your endpoint should: 1. Accept `POST` requests with `Content-Type: application/json` 2. Return an HTTP `2xx` response to acknowledge receipt 3. Process the event asynchronously (don't block the response) #### Example: Transaction Status Changed ```json { "event": "transactionStatusChanged", "transactionId": "9035ee18-052b-4176-a940-ccfe599a1829", "tenantId": "your_tenant", "complianceStatus": "Approved", "payoutStatus": "Paid", "externalId": "GMT000648512199", "timestamp": "2025-11-21T14:48:05.891186Z", "messages": [ { "message": "STANDARD - PAYMENT_DELIVERED", "createdBy": "system", "type": "SystemUpdate" } ] } ``` *** ### Best Practices #### Idempotency Your webhook handler should be **idempotent** — the same event may be delivered more than once. Use the `transactionId` and event data to deduplicate. #### Respond Quickly Return a `200 OK` as soon as you receive the webhook. Perform heavy processing (database updates, notifications, etc.) asynchronously. If your endpoint takes too long to respond, the delivery may be retried. #### Secure Your Endpoint * **Use HTTPS** — always register an `https://` URL for your webhook endpoint. * **Validate the source** — consider IP allowlisting or implementing shared-secret verification if supported by your integration agreement. * **Don't trust the payload blindly** — after receiving a `transactionStatusChanged` event, fetch the transaction via `GET /fx/transactions/{id}` to confirm the current status before taking action. #### Handle Failures Gracefully If your endpoint returns a non-2xx response or is unreachable, Inyo will retry delivery. Design your system to handle: * Duplicate deliveries (idempotency) * Out-of-order deliveries (check timestamps) * Delayed deliveries (don't assume real-time) *** ### Integration Example Here's a minimal Node.js webhook handler: ```javascript const express = require('express'); const app = express(); app.use(express.json()); app.post('/api/webhooks/inyo', async (req, res) => { // Acknowledge immediately res.status(200).send('OK'); const { event, transactionId, complianceStatus, payoutStatus } = req.body; console.log(`[Webhook] ${event} — Transaction: ${transactionId}`); console.log(` Compliance: ${complianceStatus}, Payout: ${payoutStatus}`); // Process asynchronously if (event === 'transactionStatusChanged') { if (payoutStatus === 'Paid') { // Notify sender, update your database, generate receipt await markTransactionComplete(transactionId); } else if (complianceStatus === 'Rejected') { // Handle rejection — notify sender, initiate refund if needed await handleRejection(transactionId); } } }); app.listen(3001, () => console.log('Webhook listener on :3001')); ``` *** ### All Endpoints | Operation | Method | Endpoint | | ---------------- | -------- | ---------------------------------------------- | | Register webhook | `POST` | `/organizations/{tenant}/webhooks` | | List webhooks | `GET` | `/organizations/{tenant}/webhooks` | | Delete webhook | `DELETE` | `/organizations/{tenant}/webhooks/{webhookId}` | [Interactive API Documentation](https://dev-api.inyoglobal.com/sandbox/#tag/webhook_Webhooks-Resource) --- description: >- Downloadable assets and brand resources for Inyo Global — logos, icons, and brand guidelines for partners and integrators. --- # Assets Some products require regulatory disclosures to be presented to consumers. We’ve simplified this process by providing easily embeddable resources, including markdown-ready metadata and logos in multiple sizes and formats for seamless integration. | 🛡️ Compliance Statement | [View Compliance Statement](https://terms.inyoglobal.com/compliance-statement.md) | | ------------------------ | --------------------------------------------------------------------------------- | | 🔐 Privacy Policy | [View Privacy Policy](https://terms.inyoglobal.com/privacy-policy.md) | | 🏛️ State Licenses | [View State Licenses](https://terms.inyoglobal.com/state-license.md) | | 📄 Terms of Service | [View Terms of Service](https://terms.inyoglobal.com/terms-of-service.md) | | :balloon:Inyo logos | [SVG - white and purple](https://terms.inyoglobal.com/logo-inyo-svg.zip) |