> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.redpincompany.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.redpincompany.com/_mcp/server.

# Integration guidelines

> Comprehensive guide for property developers integrating with Redpin's payment APIs

This guide outlines integration approaches for property developers and platforms enabling international property payments through Redpin's APIs.

## Common use cases

> **Info**
>
> This guide is designed for property developers, real estate platforms, and fintech companies facilitating cross-border property transactions.

## Integration approaches

Redpin offers two distinct integration methods. The following comparison helps property developers select the optimal approach:

| Aspect                                           | Hosted Solution                                                                            | API Integration                                             |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| **Development Effort (for Property Developers)** | Minimal - Pre-built KYC and payment workflows                                              | Higher - Custom implementation of all user flows            |
| **Time to Market**                               | Faster with reduced development overhead                                                   | Longer implementation timeline due to custom development    |
| **UI/UX Control**                                | Basic theming options available Advanced theming possible to align with brand requirements | Full control of UI/UX (to the Property Developers)          |
| **Technical Integration**                        | Minimal Backend and Frontend development required                                          | Full API suite integration with custom Frontend development |
| **Maintenance Overhead**                         | Lower - Platform updates handled automatically                                             | Higher - Full ownership of implementation                   |
| **Real time updates**                            | Webhook integration for payment status updates                                             | Webhook integration for payment status updates              |

### Decision framework

#### Choose Hosted Solution

**When to choose:**

* Fast time to market is priority
* Limited development resources available
* Standard payment flows meet requirements
* Minimal ongoing maintenance preferred

#### Choose API Integration

**When to choose:**

* Custom user experience requirements
* Existing payment workflows to integrate
* Full control over customer journey needed
* Advanced customization and branding required

## Hosted integration workflow

> **Info**
>
> The hosted approach simplifies implementation by redirecting customers to Redpin's hosted payment interface.

#### Step 1: Submit third-party customer data

Submit compliance data for the customer who will be making the payment:

**API Reference:** [Create Third-Party Customer](/api-reference/customers/api-integration/third-parties/create-third-party-customer)

#### Step 2: Recipient creation

Register a recipient with their personal and bank account details:

**API Reference:** [Add a New Recipient](/api-reference/customers/api-integration/recipients/add-a-new-recipient)

\<E### Request

POST [https://sandbox.currenciesdirect.com/v2/customers/\{customer_id}/recipients](https://sandbox.currenciesdirect.com/v2/customers/\{customer_id}/recipients)

**`Add an Individual Recipient in UAE using IBAN`**

```curl Add an Individual Recipient in UAE using IBAN
curl -X POST https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": {
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  },
  "recipient_bank_details": {
    "currency": "AED",
    "iban": "AE070331234567890123456"
  },
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": {
    "tax_id": "AH12345678"
  }
}'
```

**`Add an Individual Recipient in UAE using IBAN`**

```python Add an Individual Recipient in UAE using IBAN
import requests

url = "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients"

payload = {
    "name": "Ahmed Al Mansoori",
    "type": "Individual",
    "country_code": "ARE",
    "customer_own_account": "no",
    "address": {
        "address_line": "Office 2101, Jumeirah Bay X2 Tower",
        "city": "Dubai",
        "state": "Dubai",
        "post_code": "000000"
    },
    "recipient_bank_details": {
        "currency": "AED",
        "iban": "AE070331234567890123456"
    },
    "nick_name": "Ahmed UAE",
    "email": "ahmed.mansoori@example.com",
    "phone_number": "+971-501234567",
    "mobile_number": "+971-501234567",
    "client_reference_id": "recipient-123",
    "payment_reference": "Invoice#4567",
    "additional_details": { "tax_id": "AH12345678" }
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

**`Add an Individual Recipient in UAE using IBAN`**

```javascript Add an Individual Recipient in UAE using IBAN
const url = 'https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"name":"Ahmed Al Mansoori","type":"Individual","country_code":"ARE","customer_own_account":"no","address":{"address_line":"Office 2101, Jumeirah Bay X2 Tower","city":"Dubai","state":"Dubai","post_code":"000000"},"recipient_bank_details":{"currency":"AED","iban":"AE070331234567890123456"},"nick_name":"Ahmed UAE","email":"ahmed.mansoori@example.com","phone_number":"+971-501234567","mobile_number":"+971-501234567","client_reference_id":"recipient-123","payment_reference":"Invoice#4567","additional_details":{"tax_id":"AH12345678"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Add an Individual Recipient in UAE using IBAN`**

```go Add an Individual Recipient in UAE using IBAN
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients"

	payload := strings.NewReader("{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Add an Individual Recipient in UAE using IBAN`**

```ruby Add an Individual Recipient in UAE using IBAN
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

**`Add an Individual Recipient in UAE using IBAN`**

```java Add an Individual Recipient in UAE using IBAN
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}")
  .asString();
```

**`Add an Individual Recipient in UAE using IBAN`**

```php Add an Individual Recipient in UAE using IBAN
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients', [
  'body' => '{
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": {
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  },
  "recipient_bank_details": {
    "currency": "AED",
    "iban": "AE070331234567890123456"
  },
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": {
    "tax_id": "AH12345678"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

**`Add an Individual Recipient in UAE using IBAN`**

```csharp Add an Individual Recipient in UAE using IBAN
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Add an Individual Recipient in UAE using IBAN`**

````swift Add an Individual Recipient in UAE using IBAN
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": [
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  ],
  "recipient_bank_details": [
    "currency": "AED",
    "iban": "AE070331234567890123456"
  ],
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": ["tax_id": "AH12345678"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```</Accordion>

<Accordion title="Step 3: Session creation">

Create a payment session with complete transaction details, including `client_customer_ref`, `client_reference_id` and `recipient_id`:

**API Reference:** [Create Payment Session](/api-reference/customers/hosted-experience/sessions/create-payment-session)

```bash
curl -X POST https://api.redpincompany.com/v1/customers/{customer_id}/sessions \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "client_customer_ref": "CUST-1001-TXN",
  "client_reference_id": "PROJECT-PHASE-1/PROPERTY-001",
  "payment_details": {
    "type": "expression of interest",
    "amount": {
      "currency": "AED",
      "value": 5000.5
    },
    "due_date": "2025-08-15",
    "purpose_of_transaction": "PROPERTY_PURCHASE",
    "recipient_details": [
      {
        "amount": {
          "currency": "AED",
          "value": 3000.25
        },
        "payment_reference": "RentAug",
        "purpose_of_transaction": "PROPERTY_PURCHASE",
        "recipient_id": "123456"
      },
      {
        "amount": {
          "currency": "AED",
          "value": 2000.25
        },
        "payment_reference": "payment_reference",
        "purpose_of_transaction": "PROPERTY_PURCHASE",
        "recipient_id": "654321"
      }
    ]
  },
  "items": [
    {
      "item_name": "Maple Grove Estates - Downtown Toronto Loft - Installment 1 of 5",
      "amount": {
        "currency": "AED",
        "value": 3000.25
      },
      "item_ref": "INV-2025-10100",
      "item_type": "installment"
    },
    {
      "item_name": "Maple Grove Estates - Maple Street House - Installment 1 of 5",
      "amount": {
        "currency": "AED",
        "value": 2000.25
      },
      "item_ref": "INV-2025-10100",
      "item_type": "installment"
    }
  ],
  "allowed_origins": [
    "https://app.example.com"
  ]
}'
````

> **Info**
>
> **OTP Enforcement and `allowed_origins`**
>
> By default, an OTP verification screen is displayed in each session. The OTP is sent to the mobile number provided when creating the [Third-Party Customer](/api-reference/customers/api-integration/third-parties/create-third-party-customer).
>
> If `allowed_origins` is configured with one or more entries, the OTP screen is skipped only when the request's origin matches one of the trusted origins. For all other origins, OTP verification remains enforced.
>
> This mechanism enables developers to bypass OTP verification for sessions launched from trusted, developer-controlled apps while enforcing additional verification for hosted links opened outside those apps.
>
> **OTP Flow Details**
>
> * Purpose: Adds step-up verification to mitigate risk when hosted links are opened outside developer-controlled apps.
> * Trigger: Displayed immediately upon landing on the hosted link in each session.
> * Delivery: A one-time code is sent to the Third-Party Customer's registered mobile number via SMS or voice call, based on user preference.
> * Validation: The customer must enter the correct OTP to proceed.
> * Protections: Rate limiting and detection mechanisms are in place to mitigate OTP flooding or SMS pumping attempts.
>
> > **Tip**
> >
> > To keep a seamless in-app experience, provide your app origins/bundle identifiers in `allowed_origins`. For shareable links (email/CRM/WhatsApp), omit `allowed_origins` to automatically enforce OTP.

#### Step 4: Customer interaction

The session creation response provides options for customer interaction:

* **Web applications**: Redirect customers to the returned session URL
* **Embedded iframe**: Embed the session URL within your application for integrated experiences
* **Native mobile apps**: Open the URL inside an "out of process" WebView, such as `ASWebAuthenticationSession` in iOS or Android Custom Tab, making the process feel seamless within your application

Customers complete their payment journey within Redpin's hosted environment, including any required verification steps.

#### Step 5: Custom Theme Integration (optional)

You can theme the hosted experience to match your brand by sending a theme payload to the hosted session via `postMessage`.

> **Info**
>
> **Why Custom Theme Integration?**
>
> Custom theming allows you to:
>
> * **Brand Consistency**: Maintain your visual identity across the payment flow
> * **User Experience**: Provide a seamless, familiar interface for your customers
> * **Trust Building**: Reduce cognitive load by keeping users in your brand environment
> * **Conversion Optimization**: Familiar UI elements can improve payment completion rates

> **Warning**
>
> **Important Limitations**
>
> While custom themes allow branding and color/font customization, note:
>
> * Core payment flow layout and structure cannot be modified
> * Compliance and security elements are non-editable
> * Theming is client-side only and should be optimized for mobile
>
> See below for full theming limitations and supported properties.

**Supported Theme Properties:**

You can customize the following properties in your theme payload:

* **fontFamily** - Font family for all text (e.g., `"CircularXX, sans-serif"`, `"System"`)
  * Supported fonts: **CircularXX**, **Helvetica**, **Roboto**, **Arial**, **sans-serif**
* **baseFontSize** - Base font size in pixels (e.g., `16`)
* **primaryColor** - Primary brand color (hex code)
* **background** - Main background color (hex code)
* **textPrimary** - Primary text color (hex code)
* **textDanger** - Error/danger text color (hex code)
* **buttonPrimary.bg** - Primary button background color (hex code)
* **buttonPrimary.text** - Primary button text color (hex code)
* **borderRadius** - Border radius in pixels (e.g., `10`, `12`)

> **Note**
>
> Additional theme properties may be added in future updates based on partner feedback and requirements.

## How Theme Integration Works

Theme integration is a simple, flexible process that works the same way for any frontend application:

* **Create Your Theme** - Define colors, fonts, and styling that match your brand
* **Send Theme to Hosted Page** - Use postMessage (web) or JavaScript injection (mobile) to apply your theme when the payment page loads
* **Listen for Updates** - Handle communication between your app and the hosted payment page for real-time theme mode switches.

**Key Benefits for Property Developers:**

* **Universal Structure**: Same theme configuration works for web (iframe) and mobile (WebView) implementations
* **Quick Setup**: Minimal code changes required to implement theming
* **Brand Control**: Maintain visual consistency across your entire user journey

**Implementation Elements:**

**Theme Configuration:**
Your theme configuration uses the same structure regardless of platform:

```tsx
const theme = {
  fontFamily: "CircularXX, system-ui, sans-serif",
  baseFontSize: 16,
  primaryColor: "#0D6EFD",
  background: "#FFFFFF",
  textPrimary: "#111827",
  textDanger: "#EF4444",
  buttonPrimary: {
    bg: "#0D6EFD",
    text: "#FFFFFF"
  },
  borderRadius: 8
};
```

**Message Communication:**
Your app communicates with the hosted page using the same message format:

```tsx
// Send theme to hosted page
{ type: "SET_THEME", payload: theme }

// Listen for hosted page readiness
{ type: "HOSTED_READY" }
```

**Dynamic Theme Generation:**
Create themes based on user preferences or system settings:

```tsx
const getDynamicTheme = (userPreferences: any, systemTheme: 'light' | 'dark') => {
  return {
    fontFamily: userPreferences.fontFamily || "System",
    baseFontSize: userPreferences.fontSize || 16,
    primaryColor: userPreferences.brandColor || "#FF5A1F",
    background: systemTheme === 'dark' ? "#0B0F14" : "#FFFFFF",
    textPrimary: systemTheme === 'dark' ? "#F9FAFB" : "#1F2937",
    textDanger: "#EF4444",
    buttonPrimary: {
      bg: userPreferences.brandColor || "#FF5A1F",
      text: "#FFFFFF"
    },
    borderRadius: userPreferences.borderRadius || 12
  };
};
```

## Platform-Specific Implementation

Choose your platform below to see the specific implementation details:

> **Tip**
>
> Before implementing from these snippets, clone and run the React or React Native reference apps in [Quickstart Setup](/api-guide/partners/quickstart-setup#hosted-ui-integration) to see a complete hosted UI integration with theme customization.

#### React (Web)

**What You'll Build:**
An iframe-based integration that embeds the hosted payment page and applies your theme

**Implementation Steps:**

* **Embed hosted session in iframe** - Display the payment page within your app
* **Set up message communication** - Listen for when the hosted page is ready
* **Apply theme via postMessage** - Send your theme configuration to the hosted page

```tsx
import React, { useEffect, useRef, useState } from "react";

const HOSTED_ORIGIN = "https://hosted.redpin.example"; // Replace with actual origin

export function HostedCheckoutWithTheme({ sessionUrl }: { sessionUrl: string }) \{
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
  const [isReady, setIsReady] = useState(false);

  // Listen for hosted page readiness
  useEffect(() => \{
    function handleMessage(event: MessageEvent) {
      if (event.origin !== HOSTED_ORIGIN) return;
      if (event.data?.type === "HOSTED_READY") {
        setIsReady(true);
      }
    }
    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, []);

  // Send theme when ready
  useEffect(() => {
    if (!isReady || !iframeRef.current) return;
    iframeRef.current.contentWindow?.postMessage(
      { type: "SET_THEME", payload: theme },
      HOSTED_ORIGIN
    );
  }, [isReady]);

  return (
    <iframe
      ref={iframeRef}
      src={sessionUrl}
      style={{ width: "100%", height: "720px", border: 0 }}
      title="Redpin Hosted Session"
    />
  );
}
```

> **Tip**
>
> Validate `event.origin` and use the exact origin from the hosted session URL when calling `postMessage` for security.

#### React Native

**What You'll Build:**
A WebView-based integration that embeds the hosted payment page and applies your theme via JavaScript injection

**Implementation Steps:**

* **Embed hosted session in WebView** - Display the payment page within your mobile app
* **Set up message communication** - Listen for when the hosted page is ready
* **Apply theme via JavaScript injection** - Inject your theme configuration into the hosted page

```tsx
import React, { useRef, useState, useEffect } from "react";
import { View, StyleSheet, ActivityIndicator } from "react-native";
import { WebView, WebViewMessageEvent } from "react-native-webview";

const HOSTED_ORIGIN = "https://hosted.redpin.example"; // Replace with actual origin

export function HostedCheckoutWithTheme({ sessionUrl }: { sessionUrl: string }) \{
  const webViewRef = useRef<WebView | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  // JavaScript to inject theme into hosted page
  const sendThemeJs = `
    (function() {
      try {
        if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', function() {
            window.postMessage(JSON.stringify({
              type: 'SET_THEME', 
              payload: ${JSON.stringify(theme)}
            }), ${JSON.stringify(HOSTED_ORIGIN)});
          });
        } else {
          window.postMessage(JSON.stringify({
            type: 'SET_THEME', 
            payload: ${JSON.stringify(theme)}
          }), ${JSON.stringify(HOSTED_ORIGIN)});
        }
      } catch (e) {
        console.error('Theme injection failed:', e);
      }
    })();
    true;
  `;

  // Handle messages from hosted page
  const handleMessage = (event: WebViewMessageEvent) => {
    try {
      const message = JSON.parse(event.nativeEvent.data);
      if (message?.type === 'READY_FOR_THEME') {
        webViewRef.current?.injectJavaScript(sendThemeJs);
      }
    } catch (err) {
      console.warn('Invalid message received:', err);
    }
  };

  // Send theme when WebView loads
  const handleLoadEnd = () => \{
    setIsLoading(false);
    setTimeout(() => {
      webViewRef.current?.injectJavaScript(sendThemeJs);
    }, 500);
  };

  return (
    <View style={styles.container}>
      \<WebView
        ref={webViewRef}
        source={{ uri: sessionUrl }}
        onLoadEnd={handleLoadEnd}
        onMessage={handleMessage}
        originWhitelist={[HOSTED_ORIGIN]}
        javaScriptEnabled={true}
        incognito={false}
        cacheEnabled={false}
        domStorageEnabled={true}
        startInLoadingState={true}
        renderLoading=\{() => (
          <View style={styles.loadingContainer}>
            <ActivityIndicator size="large" color="#FF5A1F" />
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  loadingContainer: {
    position: 'absolute',
    top: 0, left: 0, right: 0, bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(255, 255, 255, 0.9)',
  }
});
```

> **Warning**
>
> For two-way communication, the hosted page can post updates back using `window.postMessage` (web) or `window.ReactNativeWebView.postMessage` (React Native). Ensure you validate messages and origins for security.

**Best Practices:**

1. **Performance**: Send theme payload when the hosted page loads and when theme changes occur
2. **Fallbacks**: Provide fallback values for required theme properties
3. **Validation**: Validate theme payload structure before sending
4. **Testing**: Test theme integration in both light and dark modes
5. **Accessibility**: Ensure theme colors maintain sufficient contrast ratios

**Important Notes:**

* **Fallback Theme**: If no theme is provided, the hosted page will use a default theme to ensure consistent appearance
* **Theme Updates**: You can send updated themes when user preferences change (e.g., switching between light/dark mode)

#### Step 6: Analytics consent integration (embedded hosts)

Embedded hosted sessions do not display a Consent banner. The host application owns consent collection and must forward the analytics decision to the hosted page.

Web application hosted links (opened directly in a browser) show the Consent banner. No host message is required in that case.

> **Info**
>
> This step is optional. If you omit the consent message, analytics and session diagnostics stay off. That is the default for every embedded session.

> **Warning**
>
> Do not render a second consent banner inside the hosted iframe or WebView. The host is the controller for the surrounding experience and must collect analytics consent in its own CMP before granting it to the hosted session.

Analytics consent gates product analytics and session diagnostics (including session replay). Customer consent for sharing compliance data with Redpin remains a distinct requirement. See [Compliance requirements](/api-guide/partners/integration-guidelines#compliance-requirements).

**When this applies:**

| Surface                           | Consent owner                | Host action                  |
| --------------------------------- | ---------------------------- | ---------------------------- |
| Web application hosted URL        | End user, via Consent banner | None                         |
| Embedded iframe or native WebView | Host site or host app        | Send `SET_ANALYTICS_CONSENT` |

**What is gated:**

* Product analytics
* Session diagnostics and replay

With no consent signal, those tools are not loaded. Tracking calls are no-ops.

**Host responsibilities:**

* Collect analytics consent in the host CMP before sending `analytics: true`
* Forward grant and revoke to the hosted session
* Treat sending nothing as analytics off

```mermaid
flowchart TD
  load[Hosted session load]
  load --> embed{Embedded iframe or WebView?}
  embed -->|no| consent[Consent banner]
  embed -->|yes| host[Host SET_ANALYTICS_CONSENT]
  consent --> gate[Analytics tools]
  host --> gate
  gate -->|granted| on[Analytics start]
  gate -->|denied or no signal| off[No analytics loaded]
```

## Message contract

The hosted page listens for `SET_ANALYTICS_CONSENT`. It does not emit a consent-ready event. Send the message after the iframe or WebView has loaded. Target the hosted session origin, not `*`.

```tsx
{ type: "SET_ANALYTICS_CONSENT", payload: { analytics: true } }
```

> **Note**
>
> `payload.analytics` must be a boolean. Values such as `"yes"` or `"true"` are ignored. Object payloads and JSON strings are both accepted, because WebView injection often delivers strings.

**Revocation:** send the same message with `analytics: false` at any time. Product analytics and session diagnostics stop immediately, without a page reload. Duplicate values are ignored.

**Bootstrap (optional):** native hosts that can inject JavaScript before the hosted bundle runs may set `window.__analyticsConsentMessage` to the same message shape. This avoids a race if `postMessage` arrives after first paint. Iframe parents typically cannot set this global and should use `postMessage` instead.

## Platform-specific implementation

#### React (Web)

Send consent from the parent window on iframe `onLoad`. Pass the hosted origin to `postMessage`. Send the message again if the host CMP decision changes.

```tsx
import React, { useCallback, useEffect, useRef } from "react";

const HOSTED_ORIGIN = "https://hosted.redpin.example"; // Replace with actual origin

export function HostedCheckoutWithConsent({
  sessionUrl,
  analyticsConsent,
}: {
  sessionUrl: string;
  analyticsConsent: boolean;
}) \{
  const iframeRef = useRef<HTMLIFrameElement | null>(null);

  const handleSendConsent = useCallback(() => {
    iframeRef.current?.contentWindow?.postMessage(
      {
        type: "SET_ANALYTICS_CONSENT",
        payload: { analytics: analyticsConsent },
      },
      HOSTED_ORIGIN
    );
  }, [analyticsConsent]);

  const handleLoad = () => {
    handleSendConsent();
  };

  useEffect(() => {
    handleSendConsent();
  }, [handleSendConsent]);

  return (
    <iframe
      ref={iframeRef}
      src={sessionUrl}
      onLoad={handleLoad}
      style={{ width: "100%", height: "720px", border: 0 }}
      title="Redpin Hosted Session"
    />
  );
}
```

Replace `analyticsConsent` with the decision from your CMP. To withdraw consent later, post the same message with `analytics: false`.

#### React Native

Inject the consent message into the WebView after load. JSON-stringify the payload so the hosted page can parse it.

```tsx
import React, { useRef } from "react";
import { View, StyleSheet } from "react-native";
import { WebView } from "react-native-webview";

const HOSTED_ORIGIN = "https://hosted.redpin.example"; // Replace with actual origin

const analyticsConsent = true; // Replace with your CMP decision

const sendAnalyticsConsentJs = `
  (function() {
    try {
      window.postMessage(JSON.stringify({
        type: 'SET_ANALYTICS_CONSENT',
        payload: { analytics: ${analyticsConsent} }
      }), ${JSON.stringify(HOSTED_ORIGIN)});
    } catch (e) {
      console.error('Analytics consent injection failed:', e);
    }
  })();
  true;
`;

export function HostedCheckoutWithConsent({ sessionUrl }: { sessionUrl: string }) \{
  const webViewRef = useRef<WebView | null>(null);

  const handleLoadEnd = () => {
    webViewRef.current?.injectJavaScript(sendAnalyticsConsentJs);
  };

  return (
    <View style={styles.container}>
      <WebView
        ref={webViewRef}
        source={{ uri: sessionUrl }}
        onLoadEnd={handleLoadEnd}
        originWhitelist={[HOSTED_ORIGIN]}
        javaScriptEnabled={true}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
});
```

To withdraw consent without reloading, inject the same script with `analytics: false`.

#### Step 7: Webhook subscription

Subscribe to payment status events using the Webhook Subscription API to receive real-time updates about payment status changes, eliminating the need for polling the API.

**API Reference:** [Subscribe to Webhook](/api-reference/customers/api-integration/webhooks/create-webhook)

\<E### Request

POST [https://sandbox.currenciesdirect.com/v1/webhooks](https://sandbox.currenciesdirect.com/v1/webhooks)

```curl
curl -X POST https://sandbox.currenciesdirect.com/v1/webhooks \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}'
```

```python
import requests

url = "https://sandbox.currenciesdirect.com/v1/webhooks"

payload = {
    "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
    "event_type": "PAYMENT STATUS"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://sandbox.currenciesdirect.com/v1/webhooks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"endpoint":"https://webhooks.merchantapp.com/notify/payment-status","event_type":"PAYMENT STATUS"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v1/webhooks"

	payload := strings.NewReader("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v1/webhooks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://sandbox.currenciesdirect.com/v1/webhooks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://sandbox.currenciesdirect.com/v1/webhooks', [
  'body' => '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v1/webhooks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

````swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v1/webhooks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```<Info>
Webhook subscriptions are configured at the account level and will receive events for all payment sessions. For detailed payload structure and supported status events, refer to the [Webhook Reference](/api-reference/webhooks).
</Info>

**What you'll receive:**

- Payment status updates (e.g., `AWAITING_FUNDS`, `RECEIVED_FUNDS`, `PAYMENT_COMPLETED`)
- Failed payment notifications (e.g., `CANCELLED`, `BOUNCED_BACK`)
- Real-time event notifications keyed by `event_id` and `payment_id`

**Next steps:**

For detailed implementation guidance including signature verification, idempotency handling, status processing, error handling, and best practices, see the [Reconciliation Guide](/api-guide/webhooks/reconciliation) and [Webhooks overview](/api-guide/webhooks).

<Tip>
Before going live, exercise every status transition against your handler using the [webhook event simulator](/api-guide/partners/webhook-simulator).
</Tip>

</Accordion>

<Accordion title="Step 8: Session expiration">

Monitor session expiration times and create new sessions as needed for incomplete payments.

</Accordion>

</AccordionGroup>

## API integration workflow

<Info>
The following workflow demonstrates complete payment lifecycle management through direct API calls.
</Info>

<Tip>
Before implementing this workflow, clone and run the Java or Python examples in [Quickstart Setup](/api-guide/partners/quickstart-setup#api-integration) to see a complete API integration from authentication through payment execution.
</Tip>

<AccordionGroup>

<Accordion title="Step 1: Payment method validation">

Before initiating any payment flow, determine if Redpin should be presented as a payment option.

**Display Redpin as the recommended payment method when:**
- User needs to send cross-border multi-currency transfers, **and**
- User's preferred currency is supported by Redpin, **and**
- User resides in a country supported by Redpin

</Accordion>

<Accordion title="Step 2: Submit third-party customer data">

**Submit compliance data:**
Submit compliance data for the customer who will be making the payment:

**API Reference:** [Create Third-Party Customer](/api-reference/customers/api-integration/third-parties/create-third-party-customer)

<EndpointRequestSnippet endpoint="POST /api/v1/customers/{customer_id}/third-parties" />

**Retrieve customer details:**
Retrieve compliance data and verification status for a third-party customer:

**API Reference:** [Get Third-Party Customer](/api-reference/customers/api-integration/third-parties/get-third-party-customer)

<EndpointRequestSnippet endpoint="GET /api/v1/customers/{customer_id}/third-parties/{client_customer_ref}" />

**Webhook registration:**
<E### Request

POST https://sandbox.currenciesdirect.com/v1/webhooks

```curl
curl -X POST https://sandbox.currenciesdirect.com/v1/webhooks \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}'
````

```python
import requests

url = "https://sandbox.currenciesdirect.com/v1/webhooks"

payload = {
    "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
    "event_type": "PAYMENT STATUS"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://sandbox.currenciesdirect.com/v1/webhooks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"endpoint":"https://webhooks.merchantapp.com/notify/payment-status","event_type":"PAYMENT STATUS"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v1/webhooks"

	payload := strings.NewReader("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v1/webhooks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://sandbox.currenciesdirect.com/v1/webhooks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://sandbox.currenciesdirect.com/v1/webhooks', [
  'body' => '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v1/webhooks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

````swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v1/webhooks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```</Accordion>

<Accordion title="Step 3: Customer verification status">

<Success>
If customer verification passes, continue with the payment flow. If additional verification is required, handle it before proceeding.
</Success>

**Check customer status:**
Monitor the customer status to determine if you can proceed with payment creation:

<EndpointRequestSnippet endpoint="GET /api/v1/customers/{customer_id}/third-parties/{client_customer_ref}" />

**If verification is complete:**
- Customer status is "APPROVED" (see [status field](/api-reference/customers/api-integration/third-parties/create-third-party-customer#response.body.status))
- Continue to quote generation (Step 4)

**If additional verification needed:**
- Customer status is "pending" or "failure"
- Webhook will provide required documents
- Handle document collection in your UI
- Monitor status until customer verification succeeds

</Accordion>

<Accordion title="Step 4: Quote generation">

<Tip>
Quotes typically expire after 30 minutes, so ensure timely customer decision-making.
</Tip>

**Currency selection:**
Allow customers to select their source currency, destination currency, amount, and BUY/SELL operation.

**Quote request:**

**API Reference:** [Create Quote](/api-reference/customers/api-integration/quotes/create-quote)

<EndpointRequestSnippet endpoint="POST /v3/customers/{customer_id}/quotes" />

<Success>
The system returns a quoted amount, quote_id, and expiry time. Monitor quote expiration and regenerate as needed.
</Success>

</Accordion>

<Accordion title="Step 5: Payment execution">

**Recipient registration:**
Register payment recipients with their banking details:

**API Reference:** [Add a New Recipient](/api-reference/customers/api-integration/recipients/add-a-new-recipient)

<E### Request

POST https://sandbox.currenciesdirect.com/v2/customers/{customer_id}/recipients

```curl Add an Individual Recipient in UAE using IBAN
curl -X POST https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": {
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  },
  "recipient_bank_details": {
    "currency": "AED",
    "iban": "AE070331234567890123456"
  },
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": {
    "tax_id": "AH12345678"
  }
}'
````

**`Add an Individual Recipient in UAE using IBAN`**

```python Add an Individual Recipient in UAE using IBAN
import requests

url = "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients"

payload = {
    "name": "Ahmed Al Mansoori",
    "type": "Individual",
    "country_code": "ARE",
    "customer_own_account": "no",
    "address": {
        "address_line": "Office 2101, Jumeirah Bay X2 Tower",
        "city": "Dubai",
        "state": "Dubai",
        "post_code": "000000"
    },
    "recipient_bank_details": {
        "currency": "AED",
        "iban": "AE070331234567890123456"
    },
    "nick_name": "Ahmed UAE",
    "email": "ahmed.mansoori@example.com",
    "phone_number": "+971-501234567",
    "mobile_number": "+971-501234567",
    "client_reference_id": "recipient-123",
    "payment_reference": "Invoice#4567",
    "additional_details": { "tax_id": "AH12345678" }
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

**`Add an Individual Recipient in UAE using IBAN`**

```javascript Add an Individual Recipient in UAE using IBAN
const url = 'https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"name":"Ahmed Al Mansoori","type":"Individual","country_code":"ARE","customer_own_account":"no","address":{"address_line":"Office 2101, Jumeirah Bay X2 Tower","city":"Dubai","state":"Dubai","post_code":"000000"},"recipient_bank_details":{"currency":"AED","iban":"AE070331234567890123456"},"nick_name":"Ahmed UAE","email":"ahmed.mansoori@example.com","phone_number":"+971-501234567","mobile_number":"+971-501234567","client_reference_id":"recipient-123","payment_reference":"Invoice#4567","additional_details":{"tax_id":"AH12345678"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Add an Individual Recipient in UAE using IBAN`**

```go Add an Individual Recipient in UAE using IBAN
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients"

	payload := strings.NewReader("{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Add an Individual Recipient in UAE using IBAN`**

```ruby Add an Individual Recipient in UAE using IBAN
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

**`Add an Individual Recipient in UAE using IBAN`**

```java Add an Individual Recipient in UAE using IBAN
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}")
  .asString();
```

**`Add an Individual Recipient in UAE using IBAN`**

```php Add an Individual Recipient in UAE using IBAN
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients', [
  'body' => '{
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": {
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  },
  "recipient_bank_details": {
    "currency": "AED",
    "iban": "AE070331234567890123456"
  },
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": {
    "tax_id": "AH12345678"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

**`Add an Individual Recipient in UAE using IBAN`**

```csharp Add an Individual Recipient in UAE using IBAN
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"Ahmed Al Mansoori\",\n  \"type\": \"Individual\",\n  \"country_code\": \"ARE\",\n  \"customer_own_account\": \"no\",\n  \"address\": {\n    \"address_line\": \"Office 2101, Jumeirah Bay X2 Tower\",\n    \"city\": \"Dubai\",\n    \"state\": \"Dubai\",\n    \"post_code\": \"000000\"\n  },\n  \"recipient_bank_details\": {\n    \"currency\": \"AED\",\n    \"iban\": \"AE070331234567890123456\"\n  },\n  \"nick_name\": \"Ahmed UAE\",\n  \"email\": \"ahmed.mansoori@example.com\",\n  \"phone_number\": \"+971-501234567\",\n  \"mobile_number\": \"+971-501234567\",\n  \"client_reference_id\": \"recipient-123\",\n  \"payment_reference\": \"Invoice#4567\",\n  \"additional_details\": {\n    \"tax_id\": \"AH12345678\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Add an Individual Recipient in UAE using IBAN`**

````swift Add an Individual Recipient in UAE using IBAN
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "name": "Ahmed Al Mansoori",
  "type": "Individual",
  "country_code": "ARE",
  "customer_own_account": "no",
  "address": [
    "address_line": "Office 2101, Jumeirah Bay X2 Tower",
    "city": "Dubai",
    "state": "Dubai",
    "post_code": "000000"
  ],
  "recipient_bank_details": [
    "currency": "AED",
    "iban": "AE070331234567890123456"
  ],
  "nick_name": "Ahmed UAE",
  "email": "ahmed.mansoori@example.com",
  "phone_number": "+971-501234567",
  "mobile_number": "+971-501234567",
  "client_reference_id": "recipient-123",
  "payment_reference": "Invoice#4567",
  "additional_details": ["tax_id": "AH12345678"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v2/customers/cust_123456789/recipients")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```**Payment creation:**
Create payments linking recipients to conversions:

**API Reference:** [Create Payment](/api-reference/customers/api-integration/payments/create-payment)

```bash
curl -X POST https://sandbox.currenciesdirect.com/v3/customers/cust_123456789/payments \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "client_reference_id": "PAYMENT-REF-2024-01-15T10:30:00Z",
  "purpose_of_transaction": "PROPERTY_PURCHASE",
  "amount": {
    "currency": "GBP",
    "value": 50000
  },
  "recipient_details": [
    {
      "amount": {
        "currency": "GBP",
        "value": 50000
      },
      "recipient_id": "REC-001",
      "payment_reference": "PROPERTY_PURCHASE_PAYMENT",
      "purpose_of_transaction": "PROPERTY_PURCHASE"
    }
  ],
  "quote_id": "548eb44b-5e1b-45fb-86eb-88eb5bcaa781",
  "is_third_party_payment": true,
  "client_customer_ref": "CUST-001"
}'
````

#### Step 6: Payment instruction display

**Retrieve transfer instructions:**
Obtain bank details for customer payments:

**API Reference:** [Get Payment Bank Transfer Instructions](/api-reference/customers/api-integration/payments/get-payment-bank-transfer-instructions)

\<E### Request

GET [https://sandbox.currenciesdirect.com/v3/customers/\{customer_id}/payments/\{payment_id}/bank-transfer-instructions](https://sandbox.currenciesdirect.com/v3/customers/\{customer_id}/payments/\{payment_id}/bank-transfer-instructions)

**`Domestic Transfer (VBAN)`**

```curl Domestic Transfer (VBAN)
curl -G https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions \
     -H "Authorization: Bearer <token>" \
     -d from_country=GBR
```

**`Domestic Transfer (VBAN)`**

```python Domestic Transfer (VBAN)
import requests

url = "https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions"

querystring = {"from_country":"GBR"}

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

**`Domestic Transfer (VBAN)`**

```javascript Domestic Transfer (VBAN)
const url = 'https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Domestic Transfer (VBAN)`**

```go Domestic Transfer (VBAN)
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Domestic Transfer (VBAN)`**

```ruby Domestic Transfer (VBAN)
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

**`Domestic Transfer (VBAN)`**

```java Domestic Transfer (VBAN)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Domestic Transfer (VBAN)`**

```php Domestic Transfer (VBAN)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

**`Domestic Transfer (VBAN)`**

```csharp Domestic Transfer (VBAN)
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Domestic Transfer (VBAN)`**

````swift Domestic Transfer (VBAN)
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v3/customers/020123456789/payments/987654321/bank-transfer-instructions?from_country=GBR")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```<Check>
Display the returned bank details to customers for wire transfer execution.
</Check>

</Accordion>

<Accordion title="Step 7: Payment status monitoring">

<Success>
Real-time webhook notifications ensure customers stay informed throughout the payment process.
</Success>

**Webhook notifications:**
Receive real-time updates on conversion and payment status changes for customer service and reconciliation purposes.

**API Reference:** [Get Payment Details - Status Field](/api-reference/customers/api-integration/payments/get-payment-details#response.body.status)

**Payment status examples:**

Awaiting funds (Session-Based - Hosted Flow):
```json
{
  "event_id": "evt_12345678",
  "event_type": "PAYMENT STATUS",
  "version": "v1.0.0",
  "event_timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "payment_id": "87654321",
    "status": "AWAITING_FUNDS",
    "customer_id": "cust_12345678",
    "client_customer_ref": "your_internal_ref",
    "client_reference_id": "PAY-REF-001"
  }
}
````

Payment completed (Session-Based - Hosted Flow):

```json
{
  "event_id": "evt_12345679",
  "event_type": "PAYMENT STATUS",
  "version": "v1.0.0",
  "event_timestamp": "2024-01-15T11:45:00Z",
  "data": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "payment_id": "87654321",
    "status": "PAYMENT_COMPLETED",
    "customer_id": "cust_12345678",
    "client_customer_ref": "your_internal_ref",
    "client_reference_id": "PAY-REF-001",
    "recipient_details": [
      {
        "amount": {
          "currency": "AED",
          "value": 20000.58
        },
        "recipient_id": "rec_12345678"
      }
    ]
  }
}
```

> **Note:** For API Payment flows (both Third-Party and Non-Third-Party), the `session_id` field is not present in the payload. The `client_reference_id` field is present for Third-Party Payment (Session-Based) and may be present for API Payment (Third-Party) when provided, but is not present for API Payment (Non-Third-Party). The `client_customer_ref` field is present for Third-Party Payment (Session-Based) and API Payment (Third-Party), but not for API Payment (Non-Third-Party).

## Implementation considerations

\<AccordionGroup>

#### Authentication requirements

> **Error**
>
> Both integration approaches require OAuth 2.0 authentication using Client Credentials flow.

Reference the [Authentication documentation](/api-guide/getting-started/authentication) for implementation details.

\<Accordion title="Webhook configuration">

Configure webhook subscriptions using the [Webhook Subscription API](/api-reference/customers/api-integration/webhooks/create-webhook) to receive real-time status updates for:

* Customer verification status changes
* Payment status transitions
* Conversion execution confirmations
* Third-party customer status updates

\<E### Request

POST [https://sandbox.currenciesdirect.com/v1/webhooks](https://sandbox.currenciesdirect.com/v1/webhooks)

```curl
curl -X POST https://sandbox.currenciesdirect.com/v1/webhooks \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}'
```

```python
import requests

url = "https://sandbox.currenciesdirect.com/v1/webhooks"

payload = {
    "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
    "event_type": "PAYMENT STATUS"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://sandbox.currenciesdirect.com/v1/webhooks';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"endpoint":"https://webhooks.merchantapp.com/notify/payment-status","event_type":"PAYMENT STATUS"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://sandbox.currenciesdirect.com/v1/webhooks"

	payload := strings.NewReader("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://sandbox.currenciesdirect.com/v1/webhooks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://sandbox.currenciesdirect.com/v1/webhooks")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://sandbox.currenciesdirect.com/v1/webhooks', [
  'body' => '{
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://sandbox.currenciesdirect.com/v1/webhooks");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"endpoint\": \"https://webhooks.merchantapp.com/notify/payment-status\",\n  \"event_type\": \"PAYMENT STATUS\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

````swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "endpoint": "https://webhooks.merchantapp.com/notify/payment-status",
  "event_type": "PAYMENT STATUS"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://sandbox.currenciesdirect.com/v1/webhooks")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: \{ (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```<Note>
Webhook subscriptions are account-level and apply to all transactions. See the [Webhooks overview](/api-guide/webhooks) for implementation details.
</Note></Accordion>

<Accordion title="Error handling">

Implement comprehensive error handling for:
- Quote expiration scenarios
- Customer verification failures
- Payment processing delays
- Session timeout events

See the [Error handling guide](/api-guide/getting-started/error-handling) for detailed implementation.</Accordion>

<Accordion title="Compliance requirements">

<Warning>
Partners must ensure regulatory compliance for cross-border payment facilitation.
</Warning>

**Required implementations:**
- Display of Redpin's Terms and Conditions
- Customer consent collection for data sharing (KYC and payment compliance data). This is separate from analytics consent for embedded hosted sessions. See [Analytics consent integration](/api-guide/partners/integration-guidelines#step-6-analytics-consent-integration-embedded-hosts)
- Fraud prevention message presentation based on customer jurisdiction
- Regulatory compliance for cross-border payment facilitation</Accordion>

<Accordion title="Testing environment">

<Check>
Utilize Redpin's sandbox environment for comprehensive integration testing before production deployment.
</Check>

For complete environment details, see the [Environments guide](/api-guide/getting-started/environments).</Accordion></AccordionGroup>

## Support and resources

<CardGroup cols={2}>
<Card title="Technical support" icon="light code">
- API documentation: Complete endpoint specifications available in the API Reference section
- Integration support: Contact the Partner Integrations Team
- Webhook testing: Use sandbox webhooks for development validation
</Card>

<Card title="Getting started" icon="light play">
- [Authentication guide](/api-guide/getting-started/authentication)
- [Making your first payment](/api-guide/getting-started/making-your-first-payment)
- [Error handling](/api-guide/getting-started/error-handling)
- [Sandbox to production](/api-guide/getting-started/sandbox-to-production)
</Card>
</CardGroup>
````