# Welcome to ROZO

Our story begins in the summer of 2025, in a small boba shop in San Francisco that accepted USDC.

I scanned their QR code with my mobile USDC wallet, but my app showed error.

<figure><img src="/files/46rOesqgdvjDRRZMWpEV" alt="" width="173"><figcaption></figcaption></figure>

It turned out the merchant only accept USDC on Base chain, while my wallets only support USDC on Stellar. USDC on Stellar is not the same as USDC on Base.

<figure><img src="/files/s6IevejHPEqkGqJIfmw7" alt="" width="375"><figcaption></figcaption></figure>

Stablecoins are the best form of money on the Internet, but the user experience is still stuck in the early days. USDC exists on 29 different blockchains — and that’s not even counting USDT.

To pay for a coffee, why do we need to understand 29 chains, different stablecoins, different bridges, different fees? That’s *not* how money should work.<br>

We’re building ROZO, the Visa for Stablecoins. We hide the complexity, and we are building the stablecoin abstrations. You only need to think in *intents.*

> “I want to pay $3.25 for boba with 10% tips.”
>
> “I want to buy $100 BTC.”
>
> “I want to earn 10% APY with a pool with $10M+ TVL.”
>
> “I want my AI agent to pay for this API call, per request, with no API key.”

That last one is [Agentic Payments with MPP Router](/products/agentic-payments-mpprouter): an agent holding Stellar USDC can call 90+ upstream API services — OpenAI, Anthropic, DeepSeek, Perplexity, Exa, Firecrawl, Tavily and more — and settle each request over a 402 challenge, with no subscription and no card on file.

Welcome to ROZO.

Let's hide the wires and use a new way of transacting.


# Vision and Missions

Visa was a breakthrough for the last generation of payments. It made digital commerce work at global scale.

But it was built for a world of closed networks, intermediaries, and opaque risk systems — not for a world where users directly control their own money. That is why payments still fail, accounts still get frozen, and users still do not fully control how money moves.

We believe payments can be rebuilt from first principles: user-controlled funds, open infrastructure, and fewer hidden steps between intent and settlement.

## One-line thesis

Not another layer on top of old payment rails — a rebuild of payments themselves.

## Vision

Promote economic freedom.

For users: a peer-to-peer payment network — money that moves directly between people, without middlemen deciding whether it can.

## Mission

Build a frictionless, non-custodial, open payment network.

For users: one click, one tap, one second — payments that feel as simple as sending a message.

## Values

* Users control their own funds
* Payments should not depend on black boxes and layers of intermediaries
* Open systems are better than closed networks
* Protect users through mechanisms, not promises


# ROZO Lite Paper

### Overview

Rozo is building the Visa for crypto-native payments — enabling users to tap and pay with stablecoins as effortlessly as using Apple Pay. By abstracting away the complexity of blockchains, wallets, gas fees, and token types, Rozo delivers a frictionless experience for both users and merchants, online and in the real world.

#### ROZO Intents

We hide the complexity, and it's referred as ROZO Intents (intent bsed) or stablecoin abstractions.

Intents based is the way to transact with others focusing on outcomes not processes. As the user, you only need to declare an end result without stating how to accomplish it.

### 1. 🎯 The Problem: Fragmented Stablecoin Payments

Crypto users and merchants face overwhelming fragmentation:

* Every chain has its own token formats, gas requirements, and wallet UX.
* Stablecoins are scattered across Solana, Ethereum, Base, Arbitrum, Tron, and more.
* Current crypto payments require multi-step processes: wallet approvals, token bridging, and manual chain switching.

This fragmentation severely limits crypto’s usability in real-world commerce — from cafés and coworking spaces to online SaaS and AI agents.

### 2. 🧠 Solution: Intent-Based Stablecoin Abstraction

Rozo introduces a cross-chain payment abstraction powered by intent-based infrastructure. With Rozo, users pay in one click — any token, any chain — and merchants receive stablecoins seamlessly.

Key abstractions:

* Token: Users can pay with any token they hold.
* Chain: Rozo handles bridging and routing under the hood.
* Flow: One-click checkout — no approve + send, no gas setup.

Core components:

* Intent Addresses: Deterministically generated smart contracts (via CREATE2 on EVM, PDA on Solana) that encode payment parameters.
* Liquidity Solvers: Third parties who observe incoming payments and instantly settle to the merchant, recovering funds via cross-chain bridges or swaps.

### 3. 🧱 Architecture Overview

a. Intent Address Layer

* Deterministic addresses encode {amount, fromChain, toChain, token, receiver}.
* On EVM chains: Generated via a CREATE2 smart contract factory.
* On Solana: Built using Program Derived Addresses (PDAs).

b. Execution Layer

* User sends funds to the intent address.
* Solvers detect the payment and front settlement to the merchant in 1–3 seconds.
* Rozo routes funds through protocols or p2p as needed.

***

### 4. 🧩 Use Cases

Rozo powers stablecoin payments across both online platforms and physical storefronts — without subscriptions, wallet friction, or gas management.

#### A. Online Use Cases.

**🧰 SaaS & Online Merchants - Pay as you go**

* Rozo powers “pay-as-you-go” payments for SaaS without Stripe or fiat friction.
* Users can top-up stablecoins and use balance across services — just like credits.
* Suited for indie hackers, no-code tools, and global creators who need permissionless monetization.
* Use cases like OpenRouter, Creatify.ai

**🧠 MCP & AI Services**

Rozo is compatible with the emerging x402 payment protocol launched by Coinbase, which enables instant stablecoin payments over HTTP. This allows Rozo-integrated services — including APIs, SaaS apps, and AI agents — to monetize through standard web infrastructure without wallets or subscriptions. Developers can embed Rozo’s intent-based payment system directly into HTTP 402 flows, enabling seamless microtransactions for API access, software features, or metered compute. Rozo aligns with this standard to support the next generation of agent-native commerce and programmable internet value transfer.

#### B. IRL (In-Real-Life) Use Cases

**☕️ Cafés & Restaurants, Spas, Wellness & Lifestyle Vendors**

* Tourists, nomads, and locals pay in seconds using their crypto balance — no wallet popups or tokens to approve.
* Merchants can offer promotions or loyalty via Rozo incentives (e.g., cashback, credits).

**🏬** Tap to pay via Rozo POS.

* IRL businesses are listed on Rozo’s Web App, discoverable by crypto-paying users nearby.

#### C. Privacy

Rozo is built with privacy as a core principle Instead of tying payments to identity, Rozo uses single-use intent addresses and ephemeral data flows to minimize metadata leakage. Merchants receive funds without knowing a payer’s wallet history, and users transact without exposing their holdings, device info, or behavioral patterns. For off-chain flows, Rozo supports anonymous top-ups and proxy checkout layers that resemble the privacy of handing over physical cash — giving users control over when and how they reveal information, if ever.

Join us in building the “Visa for for Stablecoins” with Tap to Pay experience.


# ROZO Intent Pay Tech Doc

<sub>Updated: 2026-05-08</sub>

## Update Notes — 2026-05-08

This revision deepens the technical architecture:

* Added deterministic invoice-resolution details, using OpenRouter/Coinbase Commerce as the first implementation and keeping the same pattern extensible to future invoice sources such as Stripe invoices.
* Added ROZO-owned LP inventory and asynchronous Stellar USDC → Base USDC rebalance, so checkout latency is optimized for payment SLA rather than bridge finality.
* Expanded failure modes to cover malformed invoices, expired charges, partial payments, duplicate submissions, LP insufficiency, network errors, provider delays, bridge/rebalance delays, and refund paths.
* Clarified Rewards/cashback as a lightweight account-based discount flow for now, kept separate from the core payment settlement path.

## 1. Summary

Today, Stellar users cannot easily pay for AI tokens. There is no direct path from Stellar USDC to OpenRouter, Claude, Gemini, ChatGPT, or other AI providers. This is the friction ROZO Intents removes.

ROZO Intents is a permissionless payment layer that lets Stellar USDC users pay for any AI service tokens via OpenRouter, and 485+ other agentic-economy services on Tempo and adjacent networks, without leaving the Stellar ecosystem and without any AI provider needing to integrate Stellar.

The system builds on ROZO's existing SCF #38 Stablecoin Abstraction API and adds three new components:

1. **Intent Extraction Layer**: parses AI provider invoices and checkout flows into a structured Stellar payment intent
2. **Settlement Adapter + Liquidity Layer**: translates a Stellar USDC payment into a Coinbase-Commerce-acceptable USDC settlement on the destination chain, using ROZO-owned liquidity so the user does not wait for bridge finality
3. **Rewards**: a lightweight cashback/discount system for repeat purchases, implemented separately from the core payment settlement path

All of it runs from a Stellar-native user experience. No provider partnership required. No bridge for the user to operate.

Existing SCF #38 infrastructure references:

* [Hacken security audit](https://hacken.io/audits/rozo/sca-rozo-sdf-audit-mar2026/)
* [Dune dashboard (live)](https://dune.com/rozointents/stellar)

***

## 2. System Architecture

<figure><img src="/files/jf5kyOznYSznq8x0ZBGP" alt="Permissionless Pay architecture"><figcaption></figcaption></figure>

### 2.1 Component Overview

```
  Stellar user via dapp
        │  paste invoice URL  /  natural-language ("buy $100 OpenRouter credits")
        ▼
  ROZO Intents UI  ──►  Intent Extraction Layer (NEW)
                              │  user signs once
                              ▼
                    Stablecoin Abstraction API (existing)
                              │
                              ▼
                    Settlement Adapter (NEW)
                              │
                ┌─────────────┴──────────────┐
                ▼                            ▼
       AI provider credited         Rewards Ledger / Issuer (NEW)
       via Coinbase Commerce        discount or cashback to user
```

### 2.2 Components

| Component                                                              | Status                       |
| ---------------------------------------------------------------------- | ---------------------------- |
| Stablecoin Abstraction API (Stellar↔Base sub-second settlement)        | ✅ Production, Hacken-audited |
| Soroban PayIn / PayOut contracts                                       | ✅ Production                 |
| ROZO settlement account for AI checkout                                | 🆕 To build                  |
| ROZO-owned LP inventory for Base-side advance                          | 🆕 To build                  |
| Passkey C-address wallet support                                       | 🆕 To build                  |
| Solver / liquidity routing                                             | ✅ Production                 |
| Public Dune dashboard + Hacken audit report for SCF #38 infrastructure | ✅ Live                       |
| Intent Extraction Layer                                                | 🆕 To build                  |
| Settlement Adapter (Coinbase Commerce + Base USDC path)                | 🆕 To build                  |
| Rewards / cashback design (account-based discount flow)                | 🆕 To build                  |
| Mobile-friendly dapp for AI purchase flow                              | 🆕 To build                  |

***

## 3. Why This Architecture Is Permissionless

The unlock: OpenRouter and most AI providers already accept crypto via Coinbase Commerce. Their checkout flow exposes a unique pay-to address, amount, asset, destination chain, charge status, and expiry. The unique charge address/reference is what correlates the payment to the order. They do not need to know that the user started from Stellar, as long as the exact funds settle to the Coinbase Commerce charge.

What ROZO does:

* Parse the Coinbase Commerce checkout (or the invoice URL the user pastes) into a structured intent
* On the Stellar side, receive USDC from the user into a ROZO settlement account
* On the destination side, use ROZO-owned LP inventory to settle the matching amount to Coinbase Commerce's Base USDC endpoint
* Rebalance Stellar USDC to Base USDC asynchronously after the provider-side payment is completed
* Confirm settlement back to the user, then apply Rewards/discounts where enabled

The integration scales horizontally — every new AI service that accepts Coinbase Commerce becomes payable from Stellar without new provider work

***

## 4. Detailed Component Design

### 4.1 Intent Extraction Layer

Takes user input (URL, natural-language, or wallet deeplink) and produces a verified, structured `PaymentIntent`.

```typescript
interface PaymentIntent {
  source: { chain: "stellar"; asset: "USDC"; account: StellarAddress };
  destination: {
    provider: "openrouter" | "anthropic" | "openai" | string;
    chain: "base" | "ethereum" | string;
    asset: "USDC";
    address: EVMAddress;
    amount: BigInt;
    reference?: string;
    expiresAt: ISOString;
  };
  metadata: {
    invoiceUrl?: string;
    coinbaseChargeId?: string;
    userCreditTarget?: string;
    rewardsBps?: number;
    ...
  };
  validation: {
    providerAllowlisted: boolean;
    coinbaseCommerceVerified: boolean;
    schemaVerified: boolean;
    referencePreserved: boolean;
    amountExact: boolean;
    ...
  };
}
```

Extraction sources:

1. **URL paste** — fetch the public Coinbase Commerce charge, extract `{payTo, amount, asset, chain, chargeId, expiresAt}`, verify `NEW` status
2. **Natural-language** — LLM parses to a known provider's top-up; structured intent always shown for human confirmation before signing
3. **Wallet deeplink** — partner SDK constructs the intent client-side, no parsing needed

#### General invoice resolution

The first implementation resolves OpenRouter invoices through Coinbase Commerce. The design is intentionally general: the same resolver pattern can support future invoice sources such as Stripe invoices, SaaS checkout pages, API top-up links, and other provider-issued payment URLs.

ROZO does not rely on an LLM to infer payment-critical fields.

1. Normalize the pasted URL and verify it matches an allowlisted OpenRouter/Coinbase Commerce pattern.
2. Fetch the Coinbase Commerce charge payload.
3. Verify charge status is payable, such as `NEW`, and re-fetch it again when the user clicks confirm.
4. Extract the exact destination chain, asset, pay-to address, amount, charge ID/reference, and expiry from the charge payload.
5. Validate the extracted data against the provider schema and per-transaction limits.
6. Display the structured intent to the user for confirmation.
7. If the user edits amount, address, reference, or provider target, discard the intent and restart validation.

For a future Stripe invoice integration, the provider adapter would follow the same pattern: fetch the invoice or payment intent from the provider source, extract exact amount, currency, invoice ID/reference, customer/payment target, status, and expiry, then produce the same internal `PaymentIntent` structure. The provider adapter changes; the ROZO intent, validation, wallet signing, liquidity settlement, and reconciliation flow remain the same.

Reference safety: the destination address and reference/charge ID are payment-critical fields. They are preserved from provider data end-to-end. The AI interface can explain the fields to the user, but it cannot infer, rewrite, repair, or invent them. On expiry / 4xx / malformed URL / schema mismatch / missing reference / user edit, ROZO blocks signing and asks the user to create or paste a fresh invoice.

Monitoring: the Intent Extraction Layer records parser success rate, provider fetch errors, schema validation failures, expired invoices, missing references, user edits, and blocked signing attempts. Alerts are triggered on abnormal failure rates or provider-specific parsing regressions, so ROZO can detect and fix invoice-resolution issues before they affect a large number of users.

### 4.2 Settlement Adapter

After the user signs on Stellar, ROZO verifies the Stellar USDC payment and then uses ROZO-owned LP inventory to make an ERC-20 USDC `transfer()` call to the AI provider's unique Coinbase Commerce pay-to address on Base, exact amount.

```
User signs → Stellar USDC received in ROZO settlement account →
ROZO verifies Stellar payment → ROZO LP calls USDC.transfer(payTo, amount) on Base →
Coinbase Commerce confirms PAID → provider credits user →
ROZO rebalances Stellar USDC to Base USDC asynchronously → Rewards/discount applied where enabled
```

* **Speed:** target payment-grade SLA measured in seconds; user does not wait for Stellar-to-Base bridge finality
* **Liquidity:** uses ROZO-owned prefunded LP inventory for provider-side payment advance
* **Rebalancing:** replenishes Base-side LP inventory asynchronously through the existing ROZO Intent API for Stellar USDC → Base USDC bridge/rebalance
* **Risk:** ROZO is exposed between Stellar payment verification, Base-side Coinbase Commerce payment, and later rebalance; mitigated by per-tx ceiling, LP inventory limits, circuit breakers, retry logic, and reconciliation monitoring

### 4.3 Liquidity Settlement and Rebalancing

ROZO Intent is the user-facing abstraction: the user says what they want to buy, ROZO turns that request or invoice into a verified payment intent, and the user signs once on Stellar. The Liquidity Layer is what makes that intent usable as real checkout. It removes the waiting time and operational uncertainty that would otherwise come from bridging or provider-side confirmation.

ROZO optimizes this flow for payments, not just bridging. For a $10 AI top-up, a usage-based API purchase, or a larger $10,000 invoice, it is not reasonable to make the user wait next to a computer for one or two minutes of bridge confirmation. Some external bridge/provider services can also halt or delay for hours. That may be acceptable for treasury movement, but it is not acceptable for financial checkout or micropayments.

To make intent payment feel like one-tap checkout, ROZO brings its own liquidity. For invoices under **$10,000**, the target is to confirm provider-side payment within seconds after Stellar payment verification, subject to risk controls and LP inventory availability. In practice, if a user pays $10,000 in Stellar USDC, ROZO can immediately pay the corresponding Base USDC amount to the provider from ROZO-owned liquidity, then use the ROZO Intent API in the background to bridge/rebalance Stellar USDC back to Base USDC. This lets ROZO provide payment reliability and success-rate guarantees without depending on third-party bridge timing for the user-facing checkout.

ROZO therefore separates user-side payment from provider-side settlement:

1. User pays Stellar USDC to the ROZO settlement account.
2. ROZO verifies the Stellar payment on-chain.
3. ROZO-owned LP inventory advances the Base USDC payment to the Coinbase Commerce charge, without waiting for Stellar-to-Base bridge completion.
4. Coinbase Commerce marks the charge paid and the AI provider credits the user.
5. ROZO asynchronously rebalances Stellar USDC to Base USDC through the ROZO Intent API to restore LP inventory.
6. The order records Stellar payment, Coinbase Commerce payment, LP advance, rebalance status, and final completion.

This makes the user-facing SLA measured in seconds while preserving an auditable reconciliation trail.

### 4.4 Rewards

Rewards are intentionally separated from settlement. A failed reward action must not block a successful AI purchase.

The initial implementation can be account-based Rewards for checkout discounts: after confirmed provider fulfillment, ROZO records `purchaseId`, user account, purchase amount, reward amount, and redemption status in the rewards ledger. On the next checkout, the user can apply Rewards as a discount, and ROZO reduces the required USDC amount or covers the discounted portion from program budget/referral revenue.

Reward events are still auditable at the order level: dashboard records can link purchase ID, purchase amount, reward amount, redemption status, and the Stellar payment that funded the order. If ROZO later enables a Stellar-native reward asset, the asset/issuer/trustline and redemption mechanics will be specified separately. They are not required for the core AI checkout settlement flow.

Sybil resistance: per-purchase rate, minimum purchase size, per-account/per-window caps, and abuse monitoring.

### 4.5 Wallet Integration

Extends the existing #38 SDK with `payAiService(intent)`, a wallet-side UI primitive showing intent + Rewards preview, webhook hooks for "purchase complete" notifications, and a partner-onboarding flow (< 1 week kickoff to live). We are not building wallets — we are giving wallets a single SDK call to add AI-purchase as a feature.

***

## 5. Data Flows & State Machines

### 5.1 Happy Path: Pay $100 of OpenRouter via paste

```
t=0     User pastes OpenRouter Coinbase Commerce charge URL
t=0.2s  ROZO parses charge → { payTo, chain: base, USDC, $100 }
t=0.4s  UI shows intent: "Pay $100 USDC → OpenRouter"
t=Xs    User signs Stellar tx → USDC received in ROZO settlement account
t=X+1s  ROZO verifies Stellar payment on-chain
t=X+2s  ROZO LP inventory calls USDC.transfer(payTo, $100) on Base
t=X+5s  Coinbase Commerce sees PAID → OpenRouter credits user
t=X+6s  ROZO records Rewards/discount eligibility where enabled
t=X+7s  UI: "Done. $100 credited."
t+async ROZO rebalances Stellar USDC → Base USDC and marks LP inventory restored
```

Total user-perceived latency target: seconds. Rebalance can complete asynchronously after checkout.

### 5.2 Order State Machine

```
created
-> parsed
-> user_confirmed
-> stellar_submitted
-> stellar_paid
-> lp_advanced
-> provider_fulfilled
-> bridge_pending
-> lp_rebalanced
-> completed
```

Failure states:

```
parse_failed
expired
user_rejected
stellar_failed
partial_payment
duplicate_payment
lp_insufficient
provider_pending
provider_failed
bridge_delayed
bridge_failed
refund_required
rewards_failed
```

### 5.3 Edge Cases & Mitigations

| Edge case                                                          | Handling                                                                                                                         |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| Malformed invoice URL                                              | Reject before signing; show a retry prompt and ask the user to paste a fresh provider URL                                        |
| Coinbase Commerce charge fetch returns 4xx / 5xx                   | Do not create a payable intent; retry fetch where safe, otherwise ask user to refresh the invoice                                |
| Charge expires after parse, before user confirms                   | Re-fetch on confirm click; if expired, show "this invoice expired, please get a new one" with a prefilled retry                  |
| Missing or changed reference / charge ID                           | Block signing; payment-critical reference fields must be preserved from provider data                                            |
| User edits the parsed intent                                       | Treated as a new intent; full re-validation + re-confirm                                                                         |
| User has insufficient Stellar USDC                                 | Pre-flight balance check; offer one-click bridge from Base USDC via existing #38 path                                            |
| Partial Stellar payment                                            | Keep order in `partial_payment`; ask user to top up or initiate refund/manual review depending on amount and timeout             |
| Duplicate payment / duplicate submit                               | Idempotency key and charge ID prevent double settlement; duplicate Stellar payment is flagged for refund/manual review           |
| LP inventory insufficient                                          | Do not advance provider-side payment; throttle new orders and show retry timing until inventory is replenished                   |
| Settlement Adapter fails on Base (gas spike, RPC failure)          | Retry with exponential backoff; if retries fail, mark `provider_failed` / `refund_required` and reconcile the Stellar-side funds |
| Stellar Horizon/RPC/network failure                                | Do not mark `stellar_paid` until transaction can be independently verified; user sees pending/retry state                        |
| Bridge/rebalance delayed                                           | User purchase remains complete after provider fulfillment; LP inventory stays marked `bridge_pending` until rebalance completes  |
| Bridge/rebalance failed                                            | Retry alternate route or manual rebalance; no user action required unless refund is triggered before provider fulfillment        |
| Coinbase Commerce charge confirms but provider doesn't credit user | Record dispute; user-facing support flow; ROZO pursues via Coinbase Commerce dispute mechanics — tracked publicly on dashboard   |
| Rewards action fails                                               | Settlement is independent of Rewards; user's purchase still completes; Rewards are retried or backfilled                         |
| Provider not on allowlist                                          | UI shows a clear "we don't recognize this provider — paying anyway is at your own risk" gate; user explicit opt-in               |
| Sybil attempts to farm Rewards via tiny purchases                  | Per-purchase minimum + per-account-per-window rate-limit + dashboard abuse monitoring                                            |

***

## 6. Stellar-Specific Design Choices

A few choices are deliberately Stellar-native:

1. User experience on Stellar mainnet. The user signs on Stellar, pays Stellar USDC, and sees a Stellar-side payment record. Base is used for OpenRouter/Coinbase Commerce settlement behind the scenes, but the user does not operate the bridge or switch chains.
2. ROZO settlement account for checkout SLA. For the OpenRouter flow, Stellar USDC currently enters a ROZO settlement account. ROZO verifies that payment before advancing Base-side provider settlement from its own LP inventory.
3. Rewards as a separate checkout discount path. Rewards can start account-based for repeat-purchase discounts and dashboard reporting. This keeps rewards separate from payment settlement, so reward operations never block a successful AI purchase.
4. Coinbase Commerce as the permissionless merchant rail. Coinbase Commerce is the publicly-supported crypto checkout used by merchants beyond AI. Once ROZO ships this for OpenRouter, the same architecture extends to any Coinbase-Commerce-accepting merchant without requiring provider-side Stellar integration.

***

## 8. References

* [ROZO production data (live)](https://dune.com/rozointents/stellar)
* [Hacken security audit (March 2026)](https://hacken.io/audits/rozo/sca-rozo-sdf-audit-mar2026/)
* [ROZO website](https://rozo.ai/)
* [ROZO GitHub](https://github.com/rozoai)
* [SCF #38 award page](https://communityfund.stellar.org/projects/recN7Zf3kGBRIHVQy)

***


# ROZO Intent Pay SDK

> **Cross-chain crypto payments made simple** – Accept payments from any blockchain with a single component

| Package                                                                  | npm                                                                   |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| [@rozoai/intent-pay](https://npmjs.org/package/@rozoai/intent-pay)       | ![NPM](https://nodei.co/npm/@rozoai%2Fintent-pay.png?compact=true)    |
| [@rozoai/intent-common](https://npmjs.org/package/@rozoai/intent-common) | ![NPM](https://nodei.co/npm/@rozoai%2Fintent-common.png?compact=true) |

## 🎯 What RozoAI Intent Pay Does

RozoAI Intent Pay SDK is a React component that lets users pay you in crypto from supported networks — Base, Polygon, Solana, Stellar, and BSC. Your users can pay with their preferred wallet, while you receive exactly what you want.

**Key Benefits:**

* ✅ **One Component** - Add `<RozoPayButton>` and you're done
* ✅ **Supported Chains** - Ethereum, Base, Polygon, BSC, Solana, and Stellar
* ✅ **Any Wallet** - MetaMask, Phantom, Coinbase Wallet, and more
* ✅ **Supported Tokens** - USDC on Base/Polygon/Solana/Stellar; USDT on BSC
* ✅ **Mobile Ready** - Works perfectly on mobile apps
* ✅ **Easy Configuration** - Smart defaults, easy customization

## Demo Video

### Pay with Stellar to Base

{% embed url="<https://rozo-videos-cdn.s3.us-east-2.amazonaws.com/rozo-video-paystellar.MP4>" %}

### Pay with Base to Solana

{% embed url="<https://rozo-videos-cdn.s3.us-east-2.amazonaws.com/rozo-video-basetosol.MP4>" %}

## 📚 Documentation

| Document                                                               | Description                                        |
| ---------------------------------------------------------------------- | -------------------------------------------------- |
| [**Quick Start Guide**](/integration/rozointentpay/quick-start)        | Get up and running in 5 minutes                    |
| [**Complete Examples**](/integration/rozointentpay/examples)           | Copy-paste ready examples for different frameworks |
| [**API Reference**](/integration/rozointentpay/api-reference)          | Complete props and configuration reference         |
| [**Stellar SEP-24 Support**](/integration/rozointentpay/stellar-sep24) | ROZO Stellar anchor hosts and supported assets     |
| [**Troubleshooting**](/integration/rozointentpay/troubleshooting)      | Common mistakes and how to avoid them              |
| [**AI Prompts & Templates**](/integration/rozointentpay/ai-prompts)    | Templates for AI services to generate code         |

## 🎬 Demo

## 🚀 Quick Example

```tsx
import { RozoPayButton } from "@rozoai/intent-pay";
import { baseUSDC } from "@rozoai/intent-common";
import { getAddress } from "viem";

<RozoPayButton
  appId="rozoDemo"
  toChain={baseUSDC.chainId}
  toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
  toToken={getAddress(baseUSDC.token)}
  toUnits="10"
  intent="Pay $10"
  onPaymentCompleted={() => alert("Payment successful! 🎉")}
/>;
```

## 🔗 Supported Chains & Tokens

For a full list of supported chains and tokens, see the [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains) page.

## 💳 Payment Methods

Supported wallets:

* **Desktop**: MetaMask, Coinbase Wallet, Rainbow, Trust Wallet, Phantom, Albedo, etc.
* **Mobile**: All wallets via deep-linking

## 🔧 Installation

```bash
npm install @rozoai/intent-pay @rozoai/intent-common @tanstack/react-query wagmi viem @creit.tech/stellar-wallets-kit @stellar/stellar-sdk
```

## 📞 Support & Resources

* 📖 **Documentation**: [docs.rozo.ai](https://docs.rozo.ai)
* 💬 **Discord**: [discord.gg/rozoai](https://discord.com/invite/EfWejgTbuU)
* 🐛 **Issues**: [GitHub Issues](https://github.com/rozoai/intent-pay/issues)
* 📧 **Email**: <support@rozo.ai>
* 🔗 **Demo**: [demo.rozo.ai](https://demo.rozo.ai/)

***

**Made with ❤️ by the RozoAI team**

*Simplifying crypto payments, one transaction at a time.*


# Quick Start Guide

Get up and running with RozoAI Intent Pay SDK in 5 minutes.

## 🚀 Installation

```bash
npm install @rozoai/intent-pay @rozoai/intent-common @tanstack/react-query wagmi viem @creit.tech/stellar-wallets-kit @stellar/stellar-sdk
```

## 📁 Basic Setup

### 1. Create Providers File

Create `src/providers.tsx`:

```tsx
"use client";

import { getDefaultConfig, RozoPayProvider } from "@rozoai/intent-pay";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { type ReactNode } from "react";
import { createConfig, WagmiProvider } from "wagmi";

export const wagmiConfig = createConfig(
  getDefaultConfig({
    appName: "Your App Name",
  })
);

const queryClient = new QueryClient();

export function Providers({ children }: { children: ReactNode }) {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <RozoPayProvider debugMode>{children}</RozoPayProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}
```

### 2. Wrap Your App

```tsx
// App.tsx or layout.tsx
import { Providers } from "./providers";

export default function App() {
  return <Providers>{/* Your app components */}</Providers>;
}
```

### 3. Add Payment Button with Dynamic Form

**Important Notes:**

* `toUnits` prop accepts human-readable amounts as strings (e.g., `"10"` for 10 USDC, no `parseFloat` needed)
* You **must** call `resetPayment()` whenever `toChain`, `toAddress`, `toToken`, or `toUnits` values change
* Use `useRozoPayUI()` hook to access the `resetPayment` function

```tsx
"use client";

import { useState, useEffect } from "react";
import { RozoPayButton, useRozoPayUI } from "@rozoai/intent-pay";
import { baseUSDC } from "@rozoai/intent-common";
import { getAddress, isAddress } from "viem";

function PaymentDemo() {
  const { resetPayment } = useRozoPayUI();
  const [formData, setFormData] = useState({
    recipientAddress: "",
    tokenAddress: baseUSDC.token,
    amount: "5",
  });

  // PayButton only visible when Address and Token Address are filled
  const canShowButton =
    formData.recipientAddress &&
    isAddress(formData.recipientAddress) &&
    formData.tokenAddress &&
    isAddress(formData.tokenAddress) &&
    formData.amount &&
    parseFloat(formData.amount) > 0;

  // IMPORTANT: Must call resetPayment() whenever toChain, toAddress, toToken, or toUnits changes
  // toUnits is a human-readable amount as string (e.g., "10" for 10 USDC, no parseFloat needed)
  useEffect(() => {
    if (canShowButton) {
      resetPayment({
        toChain: baseUSDC.chainId,
        toAddress: getAddress(formData.recipientAddress),
        toToken: getAddress(formData.tokenAddress),
        toUnits: formData.amount, // Human-readable amount as string
      });
    }
  }, [formData, canShowButton]);

  return (
    <div className="p-8 max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-6">Crypto Payment Demo</h1>

      <div className="space-y-4 mb-6">
        <div>
          <label className="block text-sm font-medium mb-1">
            Recipient Address *
          </label>
          <input
            type="text"
            className="w-full p-2 border rounded"
            placeholder="0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2"
            value={formData.recipientAddress}
            onChange={(e) =>
              setFormData((prev) => ({
                ...prev,
                recipientAddress: e.target.value,
              }))
            }
          />
        </div>

        <div>
          <label className="block text-sm font-medium mb-1">
            Token Address *
          </label>
          <input
            type="text"
            className="w-full p-2 border rounded"
            placeholder="USDC Token Address"
            value={formData.tokenAddress}
            onChange={(e) =>
              setFormData((prev) => ({ ...prev, tokenAddress: e.target.value }))
            }
          />
        </div>

        <div>
          <label className="block text-sm font-medium mb-1">Amount *</label>
          <input
            type="number"
            step="0.01"
            min="0"
            className="w-full p-2 border rounded"
            placeholder="5.00"
            value={formData.amount}
            onChange={(e) =>
              setFormData((prev) => ({ ...prev, amount: e.target.value }))
            }
          />
        </div>
      </div>

      {canShowButton && (
        <RozoPayButton
          appId="rozoDemo" // Demo app ID
          toChain={baseUSDC.chainId} // Base chain (8453)
          toAddress={getAddress(formData.recipientAddress)} // Your wallet
          toToken={getAddress(formData.tokenAddress)} // USDC on Base
          toUnits={formData.amount} // Human-readable amount as string (e.g., "10" for 10 USDC)
          intent={`Pay $${formData.amount}`} // Button text
          onPaymentStarted={(event) => {
            console.log("✅ Payment started!", event.paymentId);
          }}
          onPaymentCompleted={(event) => {
            console.log("🎉 Payment completed!", event.txHash);
            alert("Payment successful! 🎉");
          }}
          onPaymentBounced={(event) => {
            console.log("❌ Payment bounced!", event);
            alert("Payment failed. You'll receive a refund.");
          }}
        />
      )}

      {!canShowButton && (
        <div className="p-3 bg-gray-100 rounded text-sm text-gray-600">
          Please fill in all required fields to show the payment button
        </div>
      )}
    </div>
  );
}
```

## 🎯 Quick Customization

### Stellar Payout Support

For Stellar USDC payouts, use `rozoStellarUSDC` from `@rozoai/intent-common`:

```tsx
import { rozoStellarUSDC } from "@rozoai/intent-common";

<RozoPayButton
  appId="rozoDemo"
  toChain={rozoStellarUSDC.chainId} // Stellar chain (1500)
  toToken={rozoStellarUSDC.token} // Stellar USDC token
  toAddress="GABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ" // Stellar address (no getAddress needed)
  toUnits="15"
  intent="Pay $15"
/>;
```

## 📖 Next Steps

* [View complete examples](/integration/rozointentpay/examples) for different frameworks
* [Read API reference](/integration/rozointentpay/api-reference) for all available props
* [See troubleshooting guide](/integration/rozointentpay/troubleshooting) for common issues


# Complete Examples

Copy-paste ready examples for different frameworks and use cases.

## 🎯 Framework-Specific Examples

### Next.js App Router Example

```tsx
// app/providers.tsx
"use client";
import { getDefaultConfig, RozoPayProvider } from "@rozoai/intent-pay";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createConfig, WagmiProvider } from "wagmi";

const wagmiConfig = createConfig(getDefaultConfig({ appName: "Demo" }));
const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <RozoPayProvider debugMode>{children}</RozoPayProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

// app/page.tsx
("use client");
import { RozoPayButton } from "@rozoai/intent-pay";
import { baseUSDC } from "@rozoai/intent-common";
import { getAddress } from "viem";

export default function HomePage() {
  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
      toToken={getAddress(baseUSDC.token)}
      toUnits="5"
      intent="Pay $5"
      onPaymentCompleted={() => alert("Payment successful! 🎉")}
    />
  );
}
```

### Vite/CRA Example

```tsx
// src/providers.tsx
import { getDefaultConfig, RozoPayProvider } from "@rozoai/intent-pay";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createConfig, WagmiProvider } from "wagmi";

const wagmiConfig = createConfig(getDefaultConfig({ appName: "Demo" }));
const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <RozoPayProvider debugMode>{children}</RozoPayProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

// src/App.tsx
import { RozoPayButton } from "@rozoai/intent-pay";
import { baseUSDC } from "@rozoai/intent-common";
import { getAddress } from "viem";
import { Providers } from "./providers";

function PaymentApp() {
  return (
    <div style={{ padding: "2rem", textAlign: "center" }}>
      <h1>Crypto Payment Demo</h1>
      <RozoPayButton
        appId="rozoDemo"
        toChain={baseUSDC.chainId}
        toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
        toToken={getAddress(baseUSDC.token)}
        toUnits="10"
        intent="Pay $10"
        onPaymentCompleted={() => console.log("Payment completed! 🎉")}
      />
    </div>
  );
}

export default function App() {
  return (
    <Providers>
      <PaymentApp />
    </Providers>
  );
}
```

## 🎯 Use Case Examples

### E-commerce Checkout

```tsx
"use client";

import { RozoPayButton, useRozoPayUI } from "@rozoai/intent-pay";
import { baseUSDC } from "@rozoai/intent-common";
import { getAddress } from "viem";
import { useState, useEffect } from "react";

const PRODUCTS = [
  { id: 1, name: "Basic Plan", price: "5", description: "Essential features" },
  { id: 2, name: "Pro Plan", price: "25", description: "Advanced features" },
  { id: 3, name: "Enterprise", price: "100", description: "Full features" },
];

function EcommerceCheckout() {
  const { resetPayment } = useRozoPayUI();
  const [selectedProduct, setSelectedProduct] = useState(PRODUCTS[0]);

  // Reset payment when selected product changes
  useEffect(() => {
    resetPayment({
      toChain: baseUSDC.chainId,
      toAddress: getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2"),
      toToken: getAddress(baseUSDC.token),
      toUnits: selectedProduct.price,
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedProduct]);

  return (
    <div className="p-8 max-w-2xl mx-auto">
      <h1 className="text-3xl font-bold mb-8">Choose Your Plan</h1>

      <div className="grid gap-4 mb-8">
        {PRODUCTS.map((product) => (
          <div
            key={product.id}
            className={`p-4 border rounded-lg cursor-pointer ${
              selectedProduct.id === product.id
                ? "border-blue-500 bg-blue-50"
                : "border-gray-200"
            }`}
            onClick={() => setSelectedProduct(product)}
          >
            <h3 className="font-semibold">{product.name}</h3>
            <p className="text-gray-600">{product.description}</p>
            <p className="text-xl font-bold">${product.price}</p>
          </div>
        ))}
      </div>

      <RozoPayButton
        appId="rozoDemo"
        toChain={baseUSDC.chainId}
        toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
        toToken={getAddress(baseUSDC.token)}
        toUnits={selectedProduct.price}
        intent={`Buy ${selectedProduct.name}`}
        metadata={{ productId: selectedProduct.id }}
        onPaymentCompleted={(event) => {
          console.log("Purchase completed!", event);
          alert(`${selectedProduct.name} purchased successfully! 🎉`);
        }}
      />
    </div>
  );
}
```

### Donation Component

```tsx
"use client";

import { RozoPayButton, useRozoPayUI } from "@rozoai/intent-pay";
import { baseUSDC, TokenSymbol } from "@rozoai/intent-common";
import { getAddress } from "viem";
import { useState, useEffect } from "react";

const DONATION_AMOUNTS = ["5", "10", "25", "50", "100"];

function DonationComponent() {
  const { resetPayment } = useRozoPayUI();
  const [customAmount, setCustomAmount] = useState("");
  const [selectedAmount, setSelectedAmount] = useState("10");

  const finalAmount = customAmount || selectedAmount;

  // Reset payment when donation amount changes
  useEffect(() => {
    if (finalAmount && parseFloat(finalAmount) > 0) {
      resetPayment({
        toChain: baseUSDC.chainId,
        toAddress: getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2"),
        toToken: getAddress(baseUSDC.token),
        toUnits: finalAmount,
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [finalAmount]);

  return (
    <div className="p-8 max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-6">Support Our Project</h1>

      <div className="mb-6">
        <p className="text-gray-600 mb-4">Choose donation amount:</p>

        <div className="grid grid-cols-3 gap-2 mb-4">
          {DONATION_AMOUNTS.map((amount) => (
            <button
              key={amount}
              className={`p-2 border rounded ${
                selectedAmount === amount && !customAmount
                  ? "border-blue-500 bg-blue-50"
                  : "border-gray-200"
              }`}
              onClick={() => {
                setSelectedAmount(amount);
                setCustomAmount("");
              }}
            >
              ${amount}
            </button>
          ))}
        </div>

        <div className="mb-4">
          <label className="block text-sm font-medium mb-2">
            Custom amount ($):
          </label>
          <input
            type="number"
            className="w-full p-2 border border-gray-300 rounded"
            placeholder="Enter amount"
            value={customAmount}
            onChange={(e) => setCustomAmount(e.target.value)}
          />
        </div>
      </div>

      {finalAmount && parseFloat(finalAmount) > 0 && (
        <RozoPayButton
          appId="rozoDemo"
          toChain={baseUSDC.chainId}
          toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
          toToken={getAddress(baseUSDC.token)}
          toUnits={finalAmount}
          intent={`Donate $${finalAmount}`}
          preferredChains={[8453, 137]} // Prefer Base and Polygon
          preferredSymbol={[TokenSymbol.USDC, TokenSymbol.USDT]} // Prioritize USDC and USDT
          onPaymentCompleted={() => {
            alert(`Thank you for your $${finalAmount} donation! 🙏`);
          }}
        />
      )}

      {(!finalAmount || parseFloat(finalAmount) <= 0) && (
        <div className="p-3 bg-gray-100 rounded text-sm text-gray-600">
          Please select or enter a donation amount
        </div>
      )}
    </div>
  );
}
```

### Stellar Payout Example

```tsx
"use client";

import { RozoPayButton } from "@rozoai/intent-pay";
import { rozoStellarUSDC } from "@rozoai/intent-common";

export default function StellarPayment() {
  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={rozoStellarUSDC.chainId} // Stellar chain (1500)
      toToken={rozoStellarUSDC.token} // Stellar USDC token
      toAddress="GABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ" // Stellar address
      toUnits="25"
      intent="Pay $25 to Stellar"
      onPaymentCompleted={(event) => {
        console.log("Stellar payment completed!", event.txHash);
        alert("Payment successful! 🎉");
      }}
    />
  );
}
```

### Token Symbol Preference Example

```tsx
"use client";

import { RozoPayButton } from "@rozoai/intent-pay";
import { baseUSDC, TokenSymbol } from "@rozoai/intent-common";
import { getAddress } from "viem";

// Default behavior (no prop needed) - prioritizes USDC and USDT
export default function DefaultPayment() {
  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
      toToken={getAddress(baseUSDC.token)}
      toUnits="10"
      intent="Pay $10"
    />
  );
}

// Prioritize USDC and USDT across all chains
export function PreferredSymbolsPayment() {
  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
      toToken={getAddress(baseUSDC.token)}
      toUnits="10"
      intent="Pay $10"
      preferredSymbol={[TokenSymbol.USDC, TokenSymbol.USDT]}
    />
  );
}

// Prioritize EURC only
// Note: EURC can only be sent to EURC - ensure toToken is an EURC token address
export function EURCPayment() {
  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
      toToken={getAddress(baseUSDC.token)} // Must be EURC token address
      toUnits="10"
      intent="Pay $10"
      preferredSymbol={[TokenSymbol.EURC]}
    />
  );
}
```

## 📖 Next Steps

* [Check API reference](/integration/rozointentpay/api-reference) for all available props
* [See troubleshooting guide](/integration/rozointentpay/troubleshooting) for common issues
* [Use AI prompts](/integration/rozointentpay/ai-prompts) for code generation


# API Reference

Complete reference for RozoAI Intent Pay SDK props, configuration, and customization options.

## 📋 Core Props (ALWAYS REQUIRED)

```tsx
<RozoPayButton
  appId="rozoDemo" // Demo app ID
  toChain={8453} // Base chain ID
  toAddress={getAddress("0x...")} // Your wallet address
  toToken={getAddress(baseUSDC.token)} // USDC token
  toUnits="10" // $10 USDC (optional)
  intent="Pay Now" // Button text (optional)
  preferredSymbol={[TokenSymbol.USDC, TokenSymbol.USDT]} // Optional: prioritize token symbols
/>
```

### Required Props

| Prop        | Type      | Description            | Example                      |
| ----------- | --------- | ---------------------- | ---------------------------- |
| `appId`     | `string`  | Your app identifier    | `"rozoDemo"`                 |
| `toChain`   | `number`  | Destination chain ID   | `8453` (Base)                |
| `toAddress` | `Address` | Your wallet address    | `getAddress("0x742d...")`    |
| `toToken`   | `Address` | Token contract address | `getAddress(baseUSDC.token)` |

### Semi-Optional Props

| Prop      | Type     | Description                                                                                                         | Default |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------- | ------- |
| `toUnits` | `string` | Human-readable amount as string (e.g., `"10"` for 10 USDC, no `parseFloat` needed). If not provided, user prompted. | -       |
| `intent`  | `string` | Button text/payment verb                                                                                            | `"Pay"` |

## 🎯 Event Handlers (RECOMMENDED)

```tsx
<RozoPayButton
  // ... required props
  onPaymentStarted={(event) => {
    console.log("Payment started:", event.paymentId);
    // Show loading state
  }}
  onPaymentCompleted={(event) => {
    console.log("Payment completed:", event.txHash);
    // Fulfill order, show success
  }}
  onPaymentBounced={(event) => {
    console.log("Payment failed:", event);
    // Handle refund, show error
  }}
/>
```

### Event Handler Types

```tsx
interface PaymentStartedEvent {
  type: RozoPayEventType.PaymentStarted;
  paymentId: string;
  chainId: number;
  txHash: string | null;
  payment: RozoPayment;
}

interface PaymentCompletedEvent {
  type: RozoPayEventType.PaymentCompleted;
  paymentId: string;
  chainId: number;
  txHash: string;
  payment: RozoPayment;
  rozoPaymentId?: string;
}

interface PaymentBouncedEvent {
  type: RozoPayEventType.PaymentBounced;
  paymentId: string;
  chainId: number;
  txHash: string;
  payment: RozoPayment;
  rozoPaymentId?: string;
}
```

## 🔧 Optional Customization Props

### Chain & Token Preferences

```tsx
<RozoPayButton
  // ... required props
  preferredChains={[8453, 137]} // Prefer Base, Polygon
  preferredTokens={[
    // Prefer specific tokens
    { chain: 8453, address: getAddress(baseUSDC.token) },
  ]}
  preferredSymbol={[TokenSymbol.USDC, TokenSymbol.USDT]} // Prioritize USDC and USDT across all chains
/>
```

| Prop              | Type                | Description                                                                                                                         | Default        |
| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------- |
| `preferredChains` | `number[]`          | Preferred chain IDs in order                                                                                                        | -              |
| `preferredTokens` | `TokenPreference[]` | Preferred tokens with chain/address                                                                                                 | -              |
| `preferredSymbol` | `TokenSymbol[]`     | Prioritizes token symbols across all supported chains in token selection UI. If `preferredTokens` is provided, it takes precedence. | `[USDC, USDT]` |

```tsx
import { TokenSymbol } from "@rozoai/intent-common";

interface TokenPreference {
  chain: number;
  address: Address;
}
```

**Note:** `preferredSymbol` automatically finds matching tokens across all chains (Base, Polygon, Ethereum, Solana, Stellar). Only `USDC`, `USDT`, and `EURC` are supported values. Invalid symbols are filtered with a console warning.

**Important:** EURC can only be sent to EURC. When using `TokenSymbol.EURC`, ensure your `toToken` is also an EURC token address.

### Stellar Payout Support

For Stellar USDC payouts, use `rozoStellarUSDC` from `@rozoai/intent-common`:

```tsx
import { rozoStellarUSDC } from "@rozoai/intent-common";

<RozoPayButton
  appId="rozoDemo"
  toChain={rozoStellarUSDC.chainId} // Stellar chain (1500)
  toToken={rozoStellarUSDC.token} // Stellar USDC token
  toAddress="GABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ" // Stellar address (no getAddress needed)
  toUnits="15"
  intent="Pay $15"
/>;
```

**Available Payout Options:**

* **Base USDC**: Use `baseUSDC.chainId` (8453), `baseUSDC.token`, and Base address with `getAddress()`
* **Stellar USDC**: Use `rozoStellarUSDC.chainId` (1500), `rozoStellarUSDC.token`, and Stellar address (no `getAddress()` needed)

For a complete list of supported payout chains and tokens, see [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains).

### UI Customization

```tsx
<RozoPayButton
  // ... required props
  theme="minimal" // Built-in themes
  mode="auto" // Light/dark mode
  disabled={false} // Enable/disable button
  className="custom-button-class" // Custom CSS class
/>
```

| Prop        | Type      | Description      | Options                                                                                     |
| ----------- | --------- | ---------------- | ------------------------------------------------------------------------------------------- |
| `theme`     | `string`  | Built-in theme   | `"minimal"`, `"rounded"`, `"retro"`, `"midnight"`, `"web95"`, `"soft"`, `"nouns"`, `"auto"` |
| `mode`      | `string`  | Color mode       | `"light"`, `"dark"`, `"auto"`                                                               |
| `disabled`  | `boolean` | Button state     | `true`, `false`                                                                             |
| `className` | `string`  | Custom CSS class | Any valid CSS class                                                                         |

### Advanced Configuration

```tsx
<RozoPayButton
  // ... required props

  // Tracking & Metadata
  metadata={{ orderId: "123", userId: "user456" }} // Custom metadata
  externalId="order_456" // Your tracking ID
  // Smart Contract Calls
  toCallData="0x..." // Optional calldata for contract interaction
  // Payment Options
  paymentOptions={["card", "crypto"]} // Limit payment methods
  evmChains={[1, 8453, 137]} // Restrict to specific EVM chains
  // Refund Configuration
  refundAddress={getAddress("0x...")} // Address for refunds
/>
```

| Prop            | Type                  | Description                              |
| --------------- | --------------------- | ---------------------------------------- |
| `metadata`      | `Record<string, any>` | Custom data to include with payment      |
| `externalId`    | `string`              | Your internal tracking identifier        |
| `toCallData`    | `Hex`                 | Optional calldata for contract calls     |
| `evmChains`     | `number[]`            | Restrict payments to specific EVM chains |
| `refundAddress` | `Address`             | Address to receive refunds if needed     |

## 🎨 Built-in Themes

### Theme Options

```tsx
theme = "minimal"; // Clean, minimal design (default)
theme = "rounded"; // Rounded corners, modern
theme = "retro"; // Retro/vintage style
theme = "midnight"; // Dark theme
theme = "web95"; // Windows 95 nostalgic
theme = "soft"; // Soft, gentle colors
theme = "nouns"; // Nouns DAO inspired
theme = "auto"; // Matches system preference
```

### Custom Styling

```tsx
// Use className for custom styling
<RozoPayButton
  className="bg-purple-500 hover:bg-purple-600 text-white font-bold py-3 px-6 rounded-lg"
  // ... other props
/>

// Or use customTheme for advanced theming
<RozoPayButton
  customTheme={{
    colors: {
      primary: "#8B5CF6",
      secondary: "#A78BFA",
      background: "#F3F4F6",
      text: "#1F2937"
    },
    borderRadius: "12px",
    fontFamily: "Inter, sans-serif"
  }}
  // ... other props
/>
```

## 🎆 Modal Control & Behavior

### Modal Options

```tsx
<RozoPayButton
  // ... required props
  defaultOpen={true} // Open modal immediately
  closeOnSuccess={true} // Auto-close after payment
  resetOnSuccess={false} // Keep payment state after success
  connectedWalletOnly={true} // Skip payment method selection
  // Modal event handlers
  onOpen={() => console.log("Modal opened")}
  onClose={() => console.log("Modal closed")}
/>
```

| Prop                   | Type       | Description                                    | Default |
| ---------------------- | ---------- | ---------------------------------------------- | ------- |
| `defaultOpen`          | `boolean`  | Open modal automatically when component mounts | `false` |
| `closeOnSuccess`       | `boolean`  | Close modal after successful payment           | `false` |
| `resetOnSuccess`       | `boolean`  | Reset payment state after success              | `true`  |
| `connectedWalletOnly`  | `boolean`  | Skip to wallet tokens (embedded flows)         | `false` |
| `confirmationMessage`  | `string`   | Custom message on confirmation screen          | -       |
| `redirectReturnUrl`    | `string`   | Return URL for external payment providers      | -       |
| `showProcessingPayout` | `boolean`  | Show processing state after payment completion | `false` |
| `onOpen`               | `function` | Called when modal opens                        | -       |
| `onClose`              | `function` | Called when modal closes                       | -       |

## 🚀 Alternative: Pre-created Payments

### Using PayId

For server-side payment creation, use the `payId` prop instead:

```tsx
// Create payment server-side first
const payId = await createRozoPayment({
  appId: "rozoDemo",
  toChain: 8453,
  toAddress: "0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2",
  toToken: baseUSDC.token,
  toUnits: "10",
  intent: "Pay Now",
});

// Then use payId in component
<RozoPayButton
  payId={payId}
  onPaymentCompleted={(event) => {
    // Handle completion
  }}
/>;
```

| Prop    | Type     | Description            |
| ------- | -------- | ---------------------- |
| `payId` | `string` | Pre-created payment ID |

**⚠️ IMPORTANT:** You must specify EITHER `appId` (with payment params) OR `payId`, but not both.

## 🎨 Custom Component Variant

### RozoPayButton.Custom

For complete UI control, use the Custom variant with a render prop:

```tsx
<RozoPayButton.Custom
  appId="rozoDemo"
  toChain={8453}
  toAddress={getAddress("0x...")}
  toToken={getAddress(baseUSDC.token)}
  toUnits="10"
  onPaymentCompleted={(event) => {
    console.log("Payment completed:", event.txHash);
  }}
>
  {({ show, hide }) => (
    <button
      onClick={show}
      className="custom-pay-button"
      style={{
        background: "linear-gradient(45deg, #FF6B6B, #4ECDC4)",
        border: "none",
        padding: "12px 24px",
        borderRadius: "8px",
        color: "white",
        fontWeight: "bold",
        cursor: "pointer",
      }}
    >
      🚀 Pay with Rozo
    </button>
  )}
</RozoPayButton.Custom>
```

**Render Props:**

* `show()` - Function to open the payment modal
* `hide()` - Function to close the payment modal

## 🔍 TypeScript Types

### Core Types

```tsx
import type { Address, Hex } from "viem";
import { TokenSymbol } from "@rozoai/intent-common";

// Main component props (using appId)
interface RozoPayButtonProps {
  // Required
  appId: string;
  toChain: number;
  toAddress: Address;
  toToken: Address;

  // Semi-optional
  toUnits?: string;
  intent?: string;

  // Optional
  toCallData?: Hex;

  // Preferences
  preferredChains?: number[];
  preferredTokens?: TokenPreference[];
  preferredSymbol?: TokenSymbol[];
  evmChains?: number[];

  // UI
  theme?: ThemeType;
  mode?: "light" | "dark" | "auto";
  customTheme?: CustomTheme;
  disabled?: boolean;
  className?: string;

  // Tracking
  metadata?: Record<string, any>;
  externalId?: string;
  refundAddress?: Address;

  // Modal behavior
  defaultOpen?: boolean;
  closeOnSuccess?: boolean;
  resetOnSuccess?: boolean;
  connectedWalletOnly?: boolean;
  confirmationMessage?: string;
  redirectReturnUrl?: string;
  showProcessingPayout?: boolean;

  // Events
  onPaymentStarted?: (event: PaymentStartedEvent) => void;
  onPaymentCompleted?: (event: PaymentCompletedEvent) => void;
  onPaymentBounced?: (event: PaymentBouncedEvent) => void;
  onOpen?: () => void;
  onClose?: () => void;
}

type ThemeType =
  | "minimal"
  | "rounded"
  | "retro"
  | "midnight"
  | "web95"
  | "soft"
  | "nouns"
  | "auto";

interface TokenPreference {
  chain: number;
  address: Address;
}

interface CustomTheme {
  colors?: {
    primary?: string;
    secondary?: string;
    background?: string;
    text?: string;
  };
  borderRadius?: string;
  fontFamily?: string;
}
```

## 🔄 Dynamic Payment Updates

### Using resetPayment() Hook

When payment parameters (`toChain`, `toAddress`, `toToken`, or `toUnits`) change dynamically, you **must** call `resetPayment()` from the `useRozoPayUI()` hook:

```tsx
import { useRozoPayUI } from "@rozoai/intent-pay";

function PaymentComponent() {
  const { resetPayment } = useRozoPayUI();
  const [amount, setAmount] = useState("10");

  useEffect(() => {
    resetPayment({
      toChain: baseUSDC.chainId,
      toAddress: getAddress("0x742d..."),
      toToken: getAddress(baseUSDC.token),
      toUnits: amount, // Human-readable string (e.g., "10" for 10 USDC)
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [amount]); // Call resetPayment() when amount changes

  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d...")}
      toToken={getAddress(baseUSDC.token)}
      toUnits={amount}
    />
  );
}
```

**Important Notes:**

* `toUnits` accepts human-readable amounts as strings (e.g., `"10"` for 10 USDC, no `parseFloat` needed)
* You **must** call `resetPayment()` whenever `toChain`, `toAddress`, `toToken`, or `toUnits` values change
* Use `useRozoPayUI()` hook to access the `resetPayment` function

## 📖 Next Steps

* [View complete examples](/integration/rozointentpay/examples) for implementation patterns
* [Check troubleshooting guide](/integration/rozointentpay/troubleshooting) for common issues
* [Use AI prompts](/integration/rozointentpay/ai-prompts) for code generation


# Stellar SEP-24 Support

Use ROZO's Stellar anchor for standard SEP-24 deposit and withdraw flows alongside the ROZO Intent Pay SDK.

## Supported Anchor Hosts

| Environment | Host                 |
| ----------- | -------------------- |
| Mainnet     | `anchor.rozo.ai`     |
| Testnet     | `testanchor.rozo.ai` |

## Supported Assets

| Network | Asset  |
| ------- | ------ |
| Stellar | `USDC` |
| Stellar | `EURC` |

For token identifiers and chain-level details, see [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains).

## Endpoint Discovery

ROZO follows the standard Stellar SEP-24 discovery flow. Start from the anchor host and read:

```
https://anchor.rozo.ai/.well-known/stellar.toml
https://testanchor.rozo.ai/.well-known/stellar.toml
```

Your client should read `TRANSFER_SERVER_SEP0024` from `stellar.toml` instead of hardcoding a transfer server path.

## When To Use SEP-24

Use SEP-24 when your Stellar integration needs an anchor-managed deposit or withdraw flow for supported assets.

Typical cases:

* On-ramp or off-ramp style deposit and withdrawal experiences
* Wallet or app flows that already support the Stellar anchor ecosystem
* Redirect-based deposit and withdraw UX outside the main ROZO payment button flow

Use the main [Quick Start Guide](/integration/rozointentpay/quick-start) when you only need the SDK payment button flow.

## Integration Notes

* Use `anchor.rozo.ai` for production.
* Use `testanchor.rozo.ai` for testing and sandbox environments.
* Supported Stellar assets are currently `USDC` and `EURC`.
* Keep asset code handling explicit in your integration so users cannot accidentally switch between `USDC` and `EURC`.
* If you support both EVM and Stellar destinations in one app, keep your SDK payout config and your SEP-24 anchor config separate.

## Example Discovery Flow

1. Fetch the anchor's `stellar.toml`.
2. Read `TRANSFER_SERVER_SEP0024`.
3. Start the SEP-24 `deposit` or `withdraw` flow for `USDC` or `EURC`.
4. Redirect the user through the anchor flow in your wallet or app UI.
5. Track the transaction state using the anchor response and your app session state.

## Asset Selection Guidance

If your app already uses ROZO Intent Pay for payouts:

* Use the SDK payout config for checkout and direct payment collection.
* Use SEP-24 only for anchor-style deposit and withdraw steps.
* Keep `USDC` and `EURC` as separate user choices in the UI.

## Related Docs

* [Quick Start Guide](/integration/rozointentpay/quick-start)
* [Complete Examples](/integration/rozointentpay/examples)
* [API Reference](/integration/rozointentpay/api-reference)
* [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains)


# Troubleshooting

Common mistakes and solutions when implementing RozoAI Intent Pay SDK.

## ⚠️ COMMON MISTAKES TO AVOID

### ❌ Missing getAddress() Wrapper

**Problem:**

```tsx
// ❌ DON'T DO THIS - Raw strings
toAddress = "0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2";
toToken = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
```

**Solution:**

```tsx
// ✅ DO THIS - Wrapped with getAddress()
toAddress={getAddress("0x742d35Cc6634C0532925a3b8D454A3fE1C11C4e2")}
toToken={getAddress(baseUSDC.token)}
```

**Why it happens:** TypeScript requires proper Address types from viem.

### ❌ Wrong Stellar Configuration

**Problem:**

```tsx
// ❌ DON'T DO THIS - Wrong chain/token for Stellar payout
<RozoPayButton
  appId="rozoDemo"
  toChain={8453} // ❌ Wrong chain for Stellar
  toToken={getAddress(baseUSDC.token)} // ❌ Wrong token for Stellar
  toAddress={getAddress("GABC123...")} // ❌ Don't use getAddress() for Stellar
/>
```

**Solution:**

```tsx
// ✅ DO THIS - Correct Stellar config
import { rozoStellarUSDC } from "@rozoai/intent-common";

<RozoPayButton
  appId="rozoDemo"
  toChain={rozoStellarUSDC.chainId} // ✅ Stellar chain (1500)
  toToken={rozoStellarUSDC.token} // ✅ Stellar USDC token
  toAddress="GABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ" // ✅ Stellar address (no getAddress)
/>;
```

**Why it happens:** Stellar payouts require Stellar chain ID (1500) and Stellar token format. Stellar addresses should not be wrapped with `getAddress()`.

### ❌ Not Calling resetPayment() When Payment Params Change

**Problem:**

```tsx
// ❌ DON'T DO THIS - Payment params change but resetPayment() not called
function PaymentComponent() {
  const [amount, setAmount] = useState("10");

  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d...")}
      toToken={getAddress(baseUSDC.token)}
      toUnits={amount} // ❌ Changed but resetPayment() not called
    />
  );
}
```

**Solution:**

```tsx
// ✅ DO THIS - Call resetPayment() when params change
import { useRozoPayUI } from "@rozoai/intent-pay";

function PaymentComponent() {
  const { resetPayment } = useRozoPayUI();
  const [amount, setAmount] = useState("10");

  useEffect(() => {
    resetPayment({
      toChain: baseUSDC.chainId,
      toAddress: getAddress("0x742d..."),
      toToken: getAddress(baseUSDC.token),
      toUnits: amount, // ✅ resetPayment() called when amount changes
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [amount]);

  return (
    <RozoPayButton
      appId="rozoDemo"
      toChain={baseUSDC.chainId}
      toAddress={getAddress("0x742d...")}
      toToken={getAddress(baseUSDC.token)}
      toUnits={amount}
    />
  );
}
```

**Why it happens:** You **must** call `resetPayment()` from `useRozoPayUI()` hook whenever `toChain`, `toAddress`, `toToken`, or `toUnits` values change to update the payment state.

### ❌ Using parseFloat() for toUnits

**Problem:**

```tsx
// ❌ DON'T DO THIS - Using parseFloat() unnecessarily
const amount = parseFloat("10"); // ❌ Not needed
<RozoPayButton toUnits={amount.toString()} />;
```

**Solution:**

```tsx
// ✅ DO THIS - Use human-readable string directly
<RozoPayButton
  toUnits="10" // ✅ Human-readable amount as string
/>
```

**Why it happens:** The `toUnits` prop accepts human-readable amounts as strings (e.g., `"10"` for 10 USDC). No `parseFloat()` conversion is needed.

## 🐛 Debugging Tools

### Enable Debug Mode

```tsx
<RozoPayProvider debugMode={true}>{children}</RozoPayProvider>
```

This will log:

* Payment flow steps
* API requests/responses
* Wallet connection status
* Error details

### Browser Developer Tools

1. **Console Logs:**
   * Check for RozoAI debug messages
   * Look for wallet connection errors
   * Monitor network requests
2. **Network Tab:**
   * Verify API calls to `intentapi.rozo.ai`
   * Check for CORS errors
   * Monitor WebSocket connections
3. **Application Tab:**
   * Check localStorage for cached data
   * Verify wallet connection status

### Common Error Messages

| Error                    | Cause                   | Solution                               |
| ------------------------ | ----------------------- | -------------------------------------- |
| `Provider not found`     | Missing RozoPayProvider | Wrap app with providers                |
| `Invalid address format` | Raw string address      | Use `getAddress()` wrapper             |
| `Chain not supported`    | Wrong chain ID          | Use supported chain (8453, 137, 56)    |
| `Token not found`        | Wrong token address     | Verify token address for chain         |
| `Wallet not connected`   | No wallet connection    | User needs to connect wallet           |
| `Payment not updating`   | Missing resetPayment()  | Call resetPayment() when params change |

## 📞 Getting Help

If you're still experiencing issues:

1. **Check Documentation:**
   * [Examples](/integration/rozointentpay/examples) - Working code examples
   * [API Reference](/integration/rozointentpay/api-reference) - Complete prop reference
2. **Community Support:**
   * 💬 **Discord**: [discord.gg/rozoai](https://discord.com/invite/EfWejgTbuU)
   * 🐛 **GitHub Issues**: [GitHub Issues](https://github.com/rozoai/intent-pay/issues)
3. **Direct Support:**
   * 📧 **Email**: <support@rozo.ai>

When reporting issues, please include:

* Code snippet showing the problem
* Error messages from browser console
* Steps to reproduce the issue
* Framework/version information

## 📖 Next Steps

* [View complete examples](/integration/rozointentpay/examples) for working implementations
* [Check API reference](/integration/rozointentpay/api-reference) for all available options
* [Use AI prompts](/integration/rozointentpay/ai-prompts) for code generation


# AI Prompts

## 🤖 Quick Implementation Prompt

Use this prompt with any AI service to generate a complete RozoAI Intent Pay implementation:

```
Create a React component with RozoAI Intent Pay SDK for crypto payments.

Key points:
- Use demo app ID "rozoDemo" for testing
- CRITICAL: Use getDefaultConfig from @rozoai/intent-pay
  (NOT createConfig from wagmi)
- For Base USDC: use baseUSDC, wrap addresses with getAddress()
- For Stellar USDC: use rozoStellarUSDC, use Stellar addresses as strings
- toUnits: human-readable string (e.g., "10" for 10 USDC)
- Must call resetPayment() when payment params change
- Add onPaymentStarted and onPaymentCompleted handlers

For complete setup instructions and requirements, refer to:
https://docs.rozo.ai/integration/rozointentpay/quick-start.md
```

## 🚀 One-Click Generation

### Lovable

[![Generate with Lovable](https://img.shields.io/badge/Generate%20with-Lovable-FF6B6B?style=for-the-badge\&logo=rocket)](https://lovable.dev/?autosubmit=true#prompt=Create%20a%20React%20component%20with%20RozoAI%20Intent%20Pay%20SDK%20for%20crypto%20payments.%20Use%20demo%20app%20ID%20%22rozoDemo%22.%20CRITICAL%3A%20Use%20getDefaultConfig%20from%20%40rozoai%2Fintent-pay%20%28NOT%20createConfig%20from%20wagmi%29.%20For%20Base%20USDC%3A%20use%20baseUSDC%2C%20wrap%20addresses%20with%20getAddress%28%29.%20For%20Stellar%20USDC%3A%20use%20rozoStellarUSDC%2C%20use%20Stellar%20addresses%20as%20strings.%20toUnits%3A%20human-readable%20string%20%28e.g.%20%2210%22%20for%2010%20USDC%29.%20Must%20call%20resetPayment%28%29%20when%20payment%20params%20change.%20Add%20onPaymentStarted%20and%20onPaymentCompleted%20handlers.%20Use%20TypeScript%2C%20TailwindCSS%20for%20mobile-responsive%20design.%20For%20complete%20setup%2C%20refer%20to%20https%3A%2F%2Fdocs.rozo.ai%2Fintegration%2Frozointentpay%2Fquick-start.md)


# ROZO Intent Pay API

ROZO Intent Pay API. One intent API, two integration paths — Bridge & Wallet (you specify the destination address) and Merchant (preconfigured wallet).

One intent API, two integration paths. Pick the one that matches how you receive funds:

|                         | [Bridge & Wallet API](/integration/api-doc/bridge-api)             | [Merchant API](/integration/api-doc/merchant-api)                                        |
| ----------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| **Who it's for**        | Wallets, bridges, apps moving funds                                | Merchants accepting payments                                                             |
| **Destination address** | You pass `destination.receiverAddress` on every request            | Preconfigured on your merchant `appId` — never sent per request                          |
| **Auth**                | App ID                                                             | `X-API-Key` from [partners.rozo.ai](https://partners.rozo.ai)                            |
| **Start here**          | [API Quick Start](/integration/api-doc/bridge-api/api-quick-start) | [API Quick Start (Merchant)](/integration/api-doc/merchant-api/api-quick-start-merchant) |

Shared references:

* [ROZO Intents Tech Design](/integration/api-doc/rozo-intents-tech-design) — how the intent flow works under the hood.
* [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains) — chain IDs, tokens, and routes.


# Bridge & Wallet API

Bridge & Wallet API. Move funds cross-chain by specifying the destination address yourself — bridging, wallet top-up, and smart account payments.

Use this API when **you provide the destination address on every request** — bridging your own funds, topping up a wallet, or paying an arbitrary on-chain address.

Every payment request includes a `destination.receiverAddress`, and ROZO routes the funds cross-chain to it.

## Pages

* [API Quick Start](/integration/api-doc/bridge-api/api-quick-start) — bridge 1 USDC from Base to Stellar in one call.
* [Wallet Top-up](/integration/api-doc/bridge-api/wallet-topup) — `anyAmount` deposits: send any amount, the system detects it automatically.
* [Get Fees](/integration/api-doc/bridge-api/get-fees) — preview fees with `dryrun=true` before creating a payment.
* [Stellar Smart Account Payments](/integration/api-doc/bridge-api/stellar-contract-payments) — pay from Soroban contract addresses (C-addresses) to EVM and Solana.

## Webhooks

Bridge and wallet apps get the same [Webhook](/integration/api-doc/merchant-api/webhook) notifications (`payment_payin_completed`, `payment_payout_completed`) as merchants — register your app on [partners.rozo.ai](https://partners.rozo.ai) (you'll get a `wallet_<slug>` appId) and configure your webhook URL there. Without a registered appId, poll the payment status instead.

## Looking to accept payments instead?

If you are a merchant with a **preconfigured receiving wallet** (no destination address needed per request), use the [Merchant API](/integration/api-doc/merchant-api).


# API Quick Start

### Bridge 1 USDC from Base to Stellar

Base chain ID: `8453`

Stellar chain ID: `1500`

App ID (please join our discord)

API Host : <https://intentapiv4.rozo.ai/functions/v1>

<figure><img src="/files/CSt93eWZPovA1BE0TbhM" alt=""><figcaption></figcaption></figure>

### Request

```
// Curl
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "rozodevDemo",
    "orderId": "order_12345",
    "type": "exactIn",
    "display": {
        "title": "Order #12345",
        "currency": "USD"
    },
    "source": {
        "chainId": "8453",
        "tokenSymbol": "USDC",
        "amount": "1.00"
    },
    "destination": {
        "chainId": "1500",
        "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
        "tokenSymbol": "USDC"
    }
}'
```

### Response

```json
{
  "id": "c3564ae8-74e0-4007-a8f9-91a1d3e3c81d",
  "appId": "rozodevDemo",
  "orderId": "order_12345",
  "status": "payment_unpaid",
  "errorCode": null,
  "type": "exactIn",
  "createdAt": "2025-12-02T02:23:31.743+00:00",
  "updatedAt": "2025-12-02T02:23:31.743+00:00",
  "expiresAt": "2025-12-02T03:23:31.743+00:00",
  "display": {
    "title": "Order #12345",
    "description": null,
    "currency": "USD"
  },
  "source": {
    "chainId": "8453",
    "tokenSymbol": "USDC",
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "1.00",
    "receiverAddress": "0x5B63758b0954fFc9D803dEC550eCB485C9c15861",
    "receiverMemo": null,
    "fee": "0.01",
    "senderAddress": null,
    "txHash": null,
    "amountReceived": null,
    "confirmedAt": null
  },
  "destination": {
    "chainId": "1500",
    "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
    "receiverMemo": null,
    "tokenSymbol": "USDC",
    "tokenAddress": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
    "amount": "0.99",
    "txHash": null,
    "confirmedAt": null
  }
}
```


# Wallet Top-up

## Wallet Top-up v1 (Any Amount) to Stellar

The `anyAmount` payment type allows users to send any amount without specifying it upfront. The system automatically detects the received amount and calculates the output dynamically.

**API Host**: `https://intentapiv4.rozo.ai/functions/v1`

**Supported Routes**: `Ethereum`, `Arbitrum`, `Base`, `BSC`, and `Polygon` source routes to `Stellar` are supported.

**Supported Source Tokens**: `USDC` and `USDT`

## Wallet Top-up v2 (Fixed address, Any amount)

For multi-chain deposit support (Ethereum, Arbitrum, Base, BSC, Polygon) with a fixed deposit address, see the [Wallet Deposit API](https://github.com/RozoAI/rozo-docs/blob/main/integration/api-doc/deposit-api.md).

## Use Cases

* Wallet top-up flows
* Donation payments
* Flexible payment amounts

## Supported Amount Range

| Limit   | Value        |
| ------- | ------------ |
| Minimum | $0.02 USDC   |
| Maximum | $10,000 USDC |

Amounts outside this range will be rejected with `amountTooLow` or `amountTooHigh` error codes. For custom limits, please contact us.

## Create Any Amount Payment

### Base USDC → Stellar Example

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "yourAppId",
    "type": "anyAmount",
    "display": {
        "title": "Wallet Top-up",
        "currency": "USD"
    },
    "source": {
        "chainId": "8453",
        "tokenSymbol": "USDC"
    },
    "destination": {
        "chainId": "1500",
        "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
        "tokenSymbol": "USDC"
    }
}'
```

> **Note**: For `anyAmount` type, do NOT include `source.amount` or `destination.amount`. The amounts are determined after payment is received.

### Response

```json
{
  "id": "abc123-def456-789",
  "appId": "yourAppId",
  "status": "payment_unpaid",
  "type": "anyAmount",
  "source": {
    "chainId": "8453",
    "tokenSymbol": "USDC",
    "amount": "0",
    "receiverAddress": "0x5B63758b0954fFc9D803dEC550eCB485C9c15861",
    "receiverMemo": null
  },
  "destination": {
    "chainId": "1500",
    "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
    "tokenSymbol": "USDC",
    "amount": "0"
  }
}
```

### Arbitrum USDT → Stellar Example

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "yourAppId",
    "type": "anyAmount",
    "display": {
        "title": "Wallet Top-up",
        "currency": "USD"
    },
    "source": {
        "chainId": "42161",
        "tokenSymbol": "USDT"
    },
    "destination": {
        "chainId": "1500",
        "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
        "tokenSymbol": "USDC"
    }
}'
```

## Supported Source Chains

* `1` Ethereum
* `42161` Arbitrum
* `8453` Base
* `56` BSC
* `137` Polygon

## Supported Source Tokens

* `USDC`
* `USDT`

## Destination Chain

* `1500` or `stellar`

## Request Notes

* `source.chainId` must be one of the supported EVM source chains above.
* `source.tokenSymbol` can be `USDC` or `USDT`.
* `destination.chainId` must be `1500` or `stellar`.
* `destination.receiverAddress` must be a valid Stellar address.
* Current examples use Stellar `USDC` as the destination token.

For the broader token and chain matrix, see [Supported Tokens and Chains](/integration/api-doc/supported-tokens-and-chains).

## After Payment Received

Once the user sends the selected source token to the `source.receiverAddress`, the system:

1. Detects the payment amount on-chain
2. Validates the amount is within the allowed range ($0.02 - $10,000)
3. Calculates the fee based on your app tier
4. Updates the payment with actual amounts
5. Triggers the payout to the destination

The payment record is updated:

```json
{
  "id": "abc123-def456-789",
  "status": "payment_payin_completed",
  "type": "anyAmount",
  "source": {
    "amount": "100.00",
    "amountReceived": "100.00",
    "fee": "0.10"
  },
  "destination": {
    "amount": "99.90"
  }
}
```

## Payment Flow

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Create    │     │    User     │     │   Monitor   │     │   Payout    │
│   Payment   │────▶│   Sends     │────▶│   Detects   │────▶│  Triggered  │
│             │     │ USDC/USDT   │     │   Amount    │     │             │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘
       │                   │                   │                   │
       ▼                   ▼                   ▼                   ▼
  Deposit address      Any amount          Validates         Funds sent
  is generated         [$0.02-$10000]       amount range       to user's
  and returned         is accepted         and liquidity      destination
```

## Error Codes

| Error Code              | Description                                      |
| ----------------------- | ------------------------------------------------ |
| `amountTooLow`          | Received amount is below $0.02 USDC              |
| `amountTooHigh`         | Received amount exceeds $10,000 USDC             |
| `insufficientLiquidity` | Destination chain lacks liquidity for the payout |

## Best Practices

1. **Use Pusher for real-time updates**: Subscribe to payment status changes instead of polling.
2. **Handle bounced payments**: If amount is outside range or liquidity is insufficient, payment will be marked as `payment_bounced` with appropriate error code.
3. **Check payment status**: Poll `GET /payments/{id}` or use Pusher to monitor status transitions.


# Get Fees

Use the `dryrun` parameter to preview the fee calculation without creating an actual payment.

**API Host**: `https://intentapiv4.rozo.ai/functions/v1`

**Endpoint:** `POST /payment-api/payments?dryrun=true`

## Request

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments?dryrun=true' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "yourAppId",
    "type": "anyAmount",
    "source": {
        "chainId": "8453",
        "tokenSymbol": "USDC",
        "amount": "100.00"
    },
    "destination": {
        "chainId": "1500",
        "receiverAddress": "GDFLZTLVMLR3OVO4VSODYB7SGVIOI2AS652WODBCGBUQAMXXXXXXXXXX",
        "tokenSymbol": "USDC"
    }
}'
```

## Response

```json
{
  "fee": "0.10",
  "source": {
    "chainId": "8453",
    "tokenSymbol": "USDC",
    "amount": "100.00"
  },
  "destination": {
    "chainId": "1500",
    "tokenSymbol": "USDC",
    "amount": "99.90"
  }
}
```

> **Note**: The `dryrun=true` parameter returns fee details without creating a payment record.


# Stellar Smart Account Payments

Stellar Contract Payments enable payments from Stellar to EVM and Solana chains via Soroban smart contract addresses (C-addresses). Users pay by invoking a contract's `pay()` function with a unique memo, and the system triggers cross-chain payout once payment is detected.

**API Host**: `https://intentapiv4.rozo.ai/functions/v1`

## Supported Routes

| Source Chain   | Source Token | Destination Chain | Destination Token |
| -------------- | ------------ | ----------------- | ----------------- |
| Stellar (1500) | USDC         | Base (8453)       | USDC              |
| Stellar (1500) | USDC         | BNB Chain (56)    | USDT              |
| Stellar (1500) | USDC         | BNB Chain (56)    | USDC              |
| Stellar (1500) | USDC         | Ethereum (1)      | USDT              |
| Stellar (1500) | USDC         | Ethereum (1)      | USDC              |
| Stellar (1500) | USDC         | Arbitrum (42161)  | USDC              |
| Stellar (1500) | USDC         | Polygon (137)     | USDC              |
| Stellar (1500) | USDC         | Solana (501)      | USDC              |

## Overview

When using `stellar_payin_contracts` intent:

1. A unique Soroban contract address (`receiverAddressContract`) and memo (`receiverMemoContract`) are generated
2. User invokes the contract's `pay()` function with the amount and memo
3. System monitors the contract for the payment
4. Cross-chain payout is triggered once payment is detected

## Payment Intent Types

| Intent                    | Description                                                                     |
| ------------------------- | ------------------------------------------------------------------------------- |
| `stellar_payin_contracts` | User pays by invoking a Soroban contract's `pay()` function with a unique memo. |

## Create Payment with Contract Intent

### Request

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "yourAppId",
    "intent": "stellar_payin_contracts",
    "type": "exactOut",
    "display": {
        "title": "Order #12345",
        "currency": "USD"
    },
    "source": {
        "senderAddress": "CBHKUT4SOTRYXJBQIOKXSW47CU2PF56OWXCML4XBQZAZLRX6R7TTNE4R",
        "chainId": 1500,
        "tokenSymbol": "USDC"
    },
    "destination": {
        "receiverAddress": "0x5772FBe7a7817ef7F586215CA8b23b8dD22C8897",
        "chainId": 8453,
        "tokenSymbol": "USDC",
        "amount": "1"
    }
}'
```

### Response

```json
{
    "id": "ae718e1b-74d3-485b-bebc-c5208850b400",
    "appId": "yourAppId",
    "orderId": null,
    "status": "payment_unpaid",
    "errorCode": null,
    "type": "exactOut",
    "createdAt": "2026-04-04T14:56:21.279+00:00",
    "updatedAt": "2026-04-04T14:56:21.279+00:00",
    "expiresAt": "2026-04-04T15:56:21.279+00:00",
    "display": {
        "title": "Order #12345",
        "description": null,
        "currency": "USD"
    },
    "source": {
        "chainId": "1500",
        "tokenSymbol": "USDC",
        "tokenAddress": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
        "amount": "1.01",
        "receiverAddress": "CC4ING2NYT2ZKA5RGSJYPQFLWW4BTSBVADFXNBFMYBJ7GNPZVF4A6FFB",
        "receiverMemo": null,
        "fee": "0.01",
        "senderAddress": null,
        "txHash": null,
        "amountReceived": null,
        "confirmedAt": null,
        "receiverAddressContract": "CAQPKW5AUPEA4C7OERZRUCBWT5RZDSETO4PR5REVRC5MT4CF3PBSKXQC",
        "receiverMemoContract": "memo_1775314581279"
    },
    "destination": {
        "chainId": "8453",
        "receiverAddress": "0x5772FBe7a7817ef7F586215CA8b23b8dD22C8897",
        "receiverMemo": null,
        "tokenSymbol": "USDC",
        "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "amount": "1.00",
        "txHash": null,
        "confirmedAt": null
    }
}
```

### Key Response Fields

| Field                            | Description                                           |
| -------------------------------- | ----------------------------------------------------- |
| `source.receiverAddressContract` | The Soroban contract address to invoke `pay()` on     |
| `source.receiverMemoContract`    | The unique memo to pass to the `pay()` function       |
| `source.amount`                  | Total amount to pay (includes fee)                    |
| `source.fee`                     | Fee amount deducted from the source payment           |
| `destination.amount`             | Amount the receiver will get on the destination chain |

## How to Pay

After creating a payment, the user invokes the Soroban contract's `pay()` function:

```
pay(sender, amount, memo)
```

| Parameter | Value                                         | Example                                                    |
| --------- | --------------------------------------------- | ---------------------------------------------------------- |
| Contract  | `source.receiverAddressContract`              | `CAQPKW5AUPEA4C7OERZRUCBWT5RZDSETO4PR5REVRC5MT4CF3PBSKXQC` |
| `sender`  | The user's Stellar address                    | `CBHK...NE4R`                                              |
| `amount`  | `source.amount` in stroops (multiply by 10^7) | `10100000` for 1.01 USDC                                   |
| `memo`    | `source.receiverMemoContract`                 | `memo_1775314581279`                                       |

On-chain, the invocation looks like:

```
invoked contract CAQP…KXQC pay(CBHK…NE4R, 10100000i128, "memo_1775314581279"str)
```

The system detects this contract invocation and triggers the cross-chain payout to the destination address.

## Combining with Any Amount

You can combine Stellar Contract Payments with Any Amount for flexible top-up flows:

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "yourAppId",
    "type": "anyAmount",
    "intent": "stellar_payin_contracts",
    "display": {
        "title": "Flexible Top-up",
        "currency": "USD"
    },
    "source": {
        "chainId": 1500,
        "tokenSymbol": "USDC"
    },
    "destination": {
        "chainId": 8453,
        "receiverAddress": "0x1234567890abcdef1234567890abcdef12345678",
        "tokenSymbol": "USDC"
    }
}'
```

When using `anyAmount` with contract payments, any amount between $0.02 - $10,000 USDC is accepted.

## Register Transaction Hash (Optional)

For faster payment tracking and accurate sender information, you can pre-register the transaction hash and sender address after the user submits their payment:

### Request

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments/{paymentId}/payin' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "txHash": "abc123def456...",
    "fromAddress": "GABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGH"
}'
```

### Response

```json
{
  "success": true,
  "message": "Transaction hash registered successfully",
  "payment": {
    "id": "abc123-def456-789",
    "status": "payment_unpaid",
    "source": {
      "txHash": "abc123def456...",
      "senderAddress": "GABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGH"
    }
  }
}
```

## Payment Flow

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Create    │     │    User     │     │   System    │     │   Payout    │
│   Payment   │────▶│  Invokes    │────▶│   Detects   │────▶│  Triggered  │
│             │     │   pay()     │     │  Contract   │     │             │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘
       │                   │                   │                   │
       ▼                   ▼                   ▼                   ▼
  Contract addr       User calls          Contract            Funds sent
  and memo are       pay(sender,         invocation          cross-chain
  returned           amount, memo)       is detected         to destination
```

## Supported Tokens

Currently only **USDC** is supported as the source token on Stellar for contract payments.

| Token | Contract Address                                           |
| ----- | ---------------------------------------------------------- |
| USDC  | `CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75` |

## Error Handling

| Error Code            | Description                                    |
| --------------------- | ---------------------------------------------- |
| `paymentNotFound`     | Payment ID does not exist                      |
| `paymentNotUpdatable` | Payment is not in a status that allows updates |
| `invalidRequest`      | Missing or invalid request parameters          |

## Best Practices

1. **Pre-register transactions**: Use the `/payin` endpoint to register `txHash` and `fromAddress` immediately after the user submits their payment. This ensures accurate tracking.
2. **Poll for status**: After creating a payment, poll `GET /payments/{id}` to check for status updates. Look for `payment_payin_completed` and `payment_payout_completed`.
3. **Handle expiration**: Payments expire after 1 hour. Ensure your UI communicates this to users.


# Merchant API

Merchant API. Accept crypto payments into your preconfigured merchant wallet — create payments, poll status, manage API keys, and receive webhooks.

Use this API when you are a **merchant accepting payments**. Your receiving wallet address is configured once on your merchant `appId` (via [partners.rozo.ai](https://partners.rozo.ai)) — so payment requests **never include a destination address**. Customers pay from any supported chain, and you receive the token and chain you chose.

## Pages

* [API Quick Start (Merchant)](/integration/api-doc/merchant-api/api-quick-start-merchant) — create a payment and poll its status in two calls.
* [Merchant API Keys](/integration/api-doc/merchant-api/merchant-api-keys) — Admin vs Orders scope, and using keys from an AI agent.
* [API Doc (POSTMAN)](/integration/api-doc/merchant-api/api-doc-postman) — full OpenAPI spec and Postman collection.
* [Webhook](/integration/api-doc/merchant-api/webhook) — get notified on `payment_payin_completed` and `payment_payout_completed`.

## Bridging your own funds instead?

If you want to send funds to an **arbitrary destination address you specify per request**, use the [Bridge & Wallet API](/integration/api-doc/bridge-api).


# API Quick Start (Merchant)

Accept a USDC payment into your merchant account in two calls: create a payment, then poll for its status.

App ID: your merchant `appId`. You can try the examples below with our test merchant `pos_rozostudio`, then join our discord to get your own.

API Host: <https://intentapiv4.rozo.ai/functions/v1>

### 1. Create a payment

`POST /payment-api/payments`

Base chain ID: `8453`

```bash
# Curl
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments' \
--header 'Content-Type: application/json' \
--data-raw '{
    "appId": "pos_rozostudio",
    "orderId": "order-'"$(date +%s)"'",
    "type": "exactIn",
    "display": {
        "title": "Your POS test",
        "currency": "USD"
    },
    "source": {
        "chainId": "8453",
        "tokenSymbol": "USDC",
        "amount": "0.10"
    }
}'
```

The response returns a payment `id` and a `receiverAddress` on the source chain — show the customer this address (or its QR) to pay.

```json
{
  "id": "<payment_id>",
  "appId": "pos_rozostudio",
  "orderId": "order-1700000000",
  "status": "payment_unpaid",
  "type": "exactIn",
  "display": {
    "title": "Your POS test",
    "currency": "USD"
  },
  "source": {
    "chainId": "8453",
    "tokenSymbol": "USDC",
    "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "0.10",
    "receiverAddress": "<deposit_address>",
    "fee": "0.00"
  }
}
```

### 2. Check payment status

`GET /payment-api/payments/{id}`

```bash
# Curl
curl 'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments/<payment_id>'
```

Poll this endpoint until `status` becomes `payment_completed`. Common statuses:

| Status              | Meaning                                |
| ------------------- | -------------------------------------- |
| `payment_unpaid`    | Waiting for the customer to send funds |
| `payment_started`   | Funds detected on chain, confirming    |
| `payment_completed` | Settled — fulfill the order            |

> Tip: instead of polling, configure a [Webhook](/integration/api-doc/merchant-api/webhook) to get notified when the payment completes.


# Merchant API Keys

Rozo merchant API keys let you create and reconcile payments programmatically — from a POS, a backend service, or an autonomous AI agent. Every key carries a **scope** that controls what it can do. This page explains the two scopes, when to use each, the endpoints they unlock, and how to wire a key into an AI agent safely.

All keys share the `rz_live_` prefix and are passed in the `X-API-Key` header.

## Scopes at a glance

| Scope      | What it can do                                     | Where it's safe                        | Default                     |
| ---------- | -------------------------------------------------- | -------------------------------------- | --------------------------- |
| **Orders** | Create payments + read payment status              | Backend, POS, server-rendered checkout | ✅ Recommended default       |
| **Admin**  | Everything Orders can do **+ read merchant stats** | Your own backend only                  | Only when explicitly chosen |

* **Orders key** — the default and recommended choice. It can create payment orders and read their status. This is everything a checkout flow needs. Because it cannot read account-level stats or change settings, it is safe to place in a POS terminal, a backend payment service, or a server-rendered checkout.
* **Admin key** — a full-power key. In addition to creating and reading orders, it can read your merchant statistics (volume, payment counts, payouts). It is sensitive and must **never** ship to a frontend, a mobile app, or a shared device. Keep it on a server you control.

When you create a key, the scope defaults to **Orders**. You only get an Admin key if you explicitly select the Admin scope.

## API hosts

Different capabilities live behind two services. Use the right base URL for each call.

| Capability           | Service        | Base URL                                                             |
| -------------------- | -------------- | -------------------------------------------------------------------- |
| Create / read orders | `payment-api`  | `https://intentapiv4.rozo.ai/functions/v1/payment-api`               |
| Read merchant stats  | `merchant-api` | `https://aozudqtlykbhzbuzalzz.supabase.co/functions/v1/merchant-api` |

Authenticate every request with your key:

```
X-API-Key: rz_live_xxx
```

## Getting a key

1. Sign in at [partners.rozo.ai](https://partners.rozo.ai) with your email (one-time passcode / OTP login).
2. Go to **Settings → API Keys**.
3. Click **New key** and choose the scope (**Orders** by default, or **Admin** if you need stats).
4. **Copy the full key immediately — it is shown only once.** Store it in your secret manager. If you lose it, revoke it and issue a new one.

## Creating a payment order

Both Orders and Admin keys can create orders. Use the `payment-api` host.

* Leave `destination` **unset** — the payout is locked to your merchant wallet on the server side. You only declare what the customer pays (`source`).
* `orderId` is **idempotent**: re-sending the same `orderId` returns the existing order instead of creating a duplicate. Use your own internal order reference.

```bash
curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api' \
  --header 'X-API-Key: rz_live_xxx' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "type": "exactIn",
    "orderId": "order_123",
    "display": {
      "name": "Cappuccino"
    },
    "source": {
      "chainId": "8453",
      "tokenSymbol": "USDC",
      "amount": "5.00"
    }
  }'
```

The response includes the order `id`, its `status` (starts at `unpaid`), and a hosted payment link / deposit address you can present to the customer.

## Reading order status

Both scopes can read orders. Poll this endpoint to reconcile.

```bash
curl --location \
  'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments/order/<appId>/<orderId>' \
  --header 'X-API-Key: rz_live_xxx'
```

Order status moves through:

```
unpaid → payin_completed → payout_completed
```

* `unpaid` — order created, customer has not paid yet.
* `payin_completed` — the customer's funds have arrived.
* `payout_completed` — funds have settled to your merchant wallet. The order is done.

## Reading merchant stats (Admin only)

Stats live on the `merchant-api` host and require an **Admin** key. Calling this endpoint with an Orders key returns `403 scope_insufficient`.

```bash
curl --location \
  'https://aozudqtlykbhzbuzalzz.supabase.co/functions/v1/merchant-api/me/stats?period=7d' \
  --header 'X-API-Key: rz_live_xxx'
```

`period` accepts `today`, `7d`, or `30d`.

```json
{
  "period": "7d",
  "volumeUsdc": "1240.50",
  "paymentCount": 248,
  "avgOrderUsdc": "5.00",
  "netToWalletUsdc": "1228.10",
  "chart": [
    { "date": "2026-06-02", "volumeUsdc": "180.00", "paymentCount": 36 },
    { "date": "2026-06-03", "volumeUsdc": "210.50", "paymentCount": 42 }
  ]
}
```

If you call it with an Orders key:

```json
{
  "error": "scope_insufficient",
  "message": "This endpoint requires an admin-scope API key."
}
```

## Using a key from an AI agent

A common AI-native pattern is to hand a single API key to an autonomous backend agent that operates the merchant account programmatically — taking payments and reporting on them without a human in the loop.

Match the scope to what the agent does:

* **Taking payments / reconciling** → give the agent an **Orders** key. It can create collection orders for a customer and poll them to confirm settlement. Nothing more.
* **Building reports / dashboards** → give the agent an **Admin** key so it can pull `/me/stats`. Only do this if the agent runs entirely on infrastructure you control.

### Example agent workflow

A typical "take a payment and reconcile it" loop:

1. **Create the order** (Orders or Admin key):

   ```bash
   curl --location --request POST 'https://intentapiv4.rozo.ai/functions/v1/payment-api' \
     --header 'X-API-Key: rz_live_xxx' \
     --header 'Content-Type: application/json' \
     --data-raw '{
       "type": "exactIn",
       "orderId": "order_123",
       "display": { "name": "AI agent invoice" },
       "source": { "chainId": "8453", "tokenSymbol": "USDC", "amount": "5.00" }
     }'
   ```
2. **Give the customer the payment link** returned in the response.
3. **Poll for status** until it reaches `payout_completed`:

   ```bash
   curl --location \
     'https://intentapiv4.rozo.ai/functions/v1/payment-api/payments/order/yourAppId/order_123' \
     --header 'X-API-Key: rz_live_xxx'
   ```
4. **Reconcile**: when status is `payout_completed`, mark the order paid in your own system. For periodic reporting, an Admin-keyed agent calls `/me/stats?period=today` and writes the numbers to your dashboard.

In pseudocode the agent loop is:

```
order = POST /payment-api  { orderId, source }
present(order.paymentLink)
loop:
    status = GET /payment-api/payments/order/{appId}/{orderId}
    if status == "payout_completed": break
    sleep(interval)
mark_paid(order)
```

## Security

* Treat every key as a **server-side secret**. Store it in environment variables or a secret manager (not in code).
* **Never** put a key in git, in a frontend bundle, in a mobile app, in logs, or in a chat message.
* An **Admin** key is full-power — keep it on your own backend only. If your agent's code or runtime could be touched by a third party, give it an **Orders** key, never Admin.
* Keys are shown **only once** at creation. If a key leaks, **revoke it in the** [**partners.rozo.ai**](https://partners.rozo.ai) **dashboard and issue a new one** immediately.

> **Tip:** Start with an Orders key. Only mint an Admin key when you have a concrete need to read stats, and scope it to a single backend process you fully control.


# API Doc (POSTMAN)

## Overview

**Latest version for merchants — Summary**

* **OpenAPI spec (v2.0.0):** [`docs/openapi-v2.0.0.yaml`](https://github.com/RozoAI/rozo-docs/blob/main/docs/openapi-v2.0.0.yaml) — hosted in this docs repo
* **Raw download:** [`raw.githubusercontent.com/.../openapi-v2.0.0.yaml`](https://raw.githubusercontent.com/RozoAI/rozo-docs/main/docs/openapi-v2.0.0.yaml)
* **Version:** `2.0.0` (previously `1.0.0`)
* **Revision date:** `2026-05-11` (recorded in `x-revision-date`)
* **This page:** <https://docs.rozo.ai/integration/api-doc/api-doc-postman>
* **Docs source on GitHub:** [rozoai/rozo-docs](https://github.com/RozoAI/rozo-docs)

## Get Your API Key

Before calling any endpoint, you need an API key from the Rozo merchant portal.

1. Sign in at [**partners.rozo.ai**](https://partners.rozo.ai).
2. Create or open your merchant app — your `appId` will look like `merchant_<slug>` or `wallet_<slug>`.
3. Generate an API key — format: `rz_live_xxxxx`. The key is bound to that `appId`.
4. Configure your **webhook URL** in the portal (used for `payment_payin_completed` and `payment_payout_completed` events).
5. Pass the key on every `POST` request as the `X-API-Key` header:

```
X-API-Key: rz_live_xxxxx
```

> When `X-API-Key` is present, the key's `app_id` is authoritative — a stale `appId` in the request body is silently overridden. This is cross-tenant defense, so you cannot accidentally write into another merchant's namespace.

**Auth errors**

* `400 missing_api_key` — `appId` requires a key but none was provided.
* `400 invalid_api_key` — key is unknown, revoked, expired, or inactive.

## Complete Endpoint List

This is everything a merchant needs to look at.

### 1. Payment API (4 endpoints)

All paths are based on the base URL:

```
https://intentapiv4.rozo.ai/functions/v1/payment-api
```

All `POST` requests require the header:

```
X-API-Key: rz_live_xxxxx
```

| # | Method | Path                                | Purpose                                                                            |
| - | ------ | ----------------------------------- | ---------------------------------------------------------------------------------- |
| 1 | POST   | `/`                                 | Create an order — returns `id` and `source.receiverAddress` (the deposit address). |
| 2 | GET    | `/payments/{paymentId}`             | Look up an order by Rozo payment id.                                               |
| 3 | GET    | `/payments/order/{appId}/{orderId}` | Look up an order by your own `orderId` (idempotent lookup).                        |
| 4 | POST   | `/payments/{paymentId}/payin`       | (Optional) Accelerate confirmation — tell Rozo the buyer's on-chain `txHash`.      |

### 2. Webhooks (2 outbound events)

Rozo will `POST` to the URL you configure at [partners.rozo.ai](https://partners.rozo.ai), signed with HMAC-SHA256.

| Event                      | Triggered when                                              |
| -------------------------- | ----------------------------------------------------------- |
| `payment_payin_completed`  | The buyer's payment is confirmed on-chain.                  |
| `payment_payout_completed` | The merchant wallet has received the funds (final success). |

**Signature headers**

* `X-Rozo-Timestamp`
* `X-Rozo-Signature`

**Signature contents**

```
sha256(timestamp + "." + raw_body, webhook_secret)
```

## Interactive API Documentation

For the full interactive reference (try-it-out, request/response samples, schemas), open the Apidog portal:

{% embed url="<https://apidoc.rozo.ai/>" %}
ROZO Payment API — Apidog
{% endembed %}

[Open API Doc on Apidog →](https://apidoc.rozo.ai/)


# Webhook

This guide is for partners who registered on **partners.rozo.ai** and want to receive payment notifications in their own backend.

You configure your webhook URL **once** in the dashboard. From then on, every payment created under your `appId` automatically uses that URL and the long-lived HMAC secret you got at registration — you never pass `webhookUrl` or `webhookSecret` on individual payment requests.

***

## 1. Concepts

| Term           | Meaning                                                                                                                                          |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `appId`        | Your account identifier. Derived from your slug at registration: `merchant_<slug>` or `wallet_<slug>`                                            |
| Webhook URL    | The HTTPS endpoint Rozo POSTs payment events to                                                                                                  |
| Webhook secret | A 64-character hex string used to sign every webhook to your URL. Generated by Rozo at registration, rotatable from the dashboard                |
| Fingerprint    | `sha256(secret)` (hex). Shown in the dashboard so you can confirm which secret your server is currently using **without** exposing the raw value |

The raw secret is shown to you **once** — at registration and again when you rotate. After that, only the fingerprint is visible.

***

## 2. Get Your Webhook Secret

### At registration

When you complete registration on partners.rozo.ai, the response includes:

```json
{
  "developerId": "...",
  "appId": "merchant_hellocafe",
  "webhookUrl": "https://hellocafe.com/rozo/webhook",
  "webhookSecret": "a1b2c3d4...64-hex-chars",
  "webhookSecretFingerprint": "sha256:abcd1234...full-hex"
}
```

**Copy the `webhookSecret` immediately.** It is not shown again.

### If you lost it — rotate

1. Sign in to [partners.rozo.ai](https://partners.rozo.ai).
2. Open **Settings → Webhooks**.
3. Click **Rotate secret** and confirm.
4. Copy the new secret. The old one stops working **immediately** — there is no grace period.

The dashboard always shows the fingerprint of the **active** secret and the timestamp of the last rotation, so you can confirm at a glance which version your server is configured for.

***

## 3. Set or Update Your Webhook URL

Either at registration (the `webhookUrl` field on `POST /merchant-api/register`) or later from **Settings → Webhooks**.

Requirements:

* Must start with `https://` (HTTP is rejected).
* Must be reachable from the public internet.
* Should return a `2xx` response **quickly**. If your processing is slow, return `200` immediately and process asynchronously.

You can change the URL at any time without rotating the secret. The change applies to **new** payment events; in-flight events for already-completed payments are not re-delivered, and pre-existing payments retain whatever URL was active when they were created.

***

## 4. What Triggers a Webhook

Rozo sends two webhook events per payment, both with the envelope described in §5:

| Event `type`               | Trigger                                                         |
| -------------------------- | --------------------------------------------------------------- |
| `payment_payin_completed`  | Buyer's payin tx confirmed on the source chain                  |
| `payment_payout_completed` | Payout tx confirmed on the destination chain (terminal success) |

Failure terminals (`payment_bounced` / `payment_expired` / `payment_refunded`) and intermediate states (`payment_started`, `payment_bridging`, `payment_payout_started`) do **not** generate a webhook. Your server must poll `GET /payment-api/payments/{id}` to observe those.

> **Failure handling is on you.** Schedule a periodic `GET /payment-api/payments/{id}` for any payment that hasn't reached a success terminal within your expected window — see §7 Delivery Semantics.

***

## 5. Request Format

Rozo sends a `POST` request with these headers:

```
Content-Type: application/json
X-Rozo-Timestamp: 1746282100000
X-Rozo-Signature: sha256=a1b2c3d4...
```

| Header             | Description                                                     |
| ------------------ | --------------------------------------------------------------- |
| `X-Rozo-Timestamp` | Unix timestamp in **milliseconds** when Rozo signed the request |
| `X-Rozo-Signature` | `sha256=` followed by the HMAC-SHA256 hex signature             |

### Body

The body is a small envelope wrapped around the same Payment object you get from `GET /payment-api/payments/{id}`. Reconciliation against your stored order is a key-by-key compare on `data`.

```json
{
  "event_id": "9d4f2e0c-7a55-4b1b-8e2a-6c1f0a5d8e30",
  "type": "payment_payout_completed",
  "timestamp": "2026-05-08T12:34:32.501Z",
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "appId": "merchant_hellocafe",
    "orderId": "order-2026-1042",
    "status": "payment_payout_completed",
    "type": "exactIn",
    "createdAt": "2026-05-08T12:30:00.000Z",
    "updatedAt": "2026-05-08T12:34:32.000Z",
    "expiresAt": "2026-05-08T13:00:00.000Z",
    "display": {
      "title": "Order #1042",
      "description": "Coffee + pastry",
      "currency": "USD"
    },
    "source": {
      "chainId": "8453",
      "tokenSymbol": "USDC",
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "10.00",
      "receiverAddress": "0xForwarderAddressOnBase...",
      "fee": "0.05",
      "senderAddress": "0x1234567890123456789012345678901234567890",
      "txHash": "0xabcd1234...",
      "amountReceived": "10.00",
      "confirmedAt": "2026-05-08T12:31:55.000Z"
    },
    "destination": {
      "chainId": "1500",
      "receiverAddress": "GMERCHANTSTELLARWALLET...",
      "tokenSymbol": "USDC",
      "tokenAddress": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
      "amount": "9.95",
      "txHash": "3YgVxWDRKtWnzKNnXR6...",
      "confirmedAt": "2026-05-08T12:34:30.000Z"
    }
  }
}
```

#### Envelope fields

| Field       | Notes                                                                                                                                                                           |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`  | Globally unique delivery id. Use this as your **idempotency key**. Equal to the `deliveryId` shown in the dashboard's delivery log — paste it there to find the matching record |
| `type`      | `payment_payin_completed` or `payment_payout_completed` (see §4)                                                                                                                |
| `timestamp` | ISO-8601 (UTC) when Rozo emitted this event                                                                                                                                     |
| `data`      | The Payment object, identical to `GET /payment-api/payments/{data.id}` (with the fields below stripped)                                                                         |

#### Fields stripped from `data`

These are **omitted** from webhook payloads even though they appear on `GET /payment-api/payments/{id}`:

* `metadata` — internal channel only.
* `webhookUrl`, `webhookSecretSource` — self-referential.
* `paymentLink` — you generated this flow; you don't need the link.
* `merchant` — branding fields you already configured.

If you need any of the above, fetch them via `GET /payment-api/payments/{data.id}`.

#### Field presence by `type`

`data` always carries the full Payment schema — fields that haven't been populated yet are sent as `null`, never omitted. This matches the shape `GET /payment-api/payments/{id}` returns, so reconcile logic can be byte-for-byte identical.

| Field                          | `payment_payin_completed` | `payment_payout_completed` |
| ------------------------------ | ------------------------- | -------------------------- |
| `data.source.txHash`           | populated                 | populated                  |
| `data.source.senderAddress`    | populated                 | populated                  |
| `data.source.amountReceived`   | populated                 | populated                  |
| `data.source.confirmedAt`      | populated                 | populated                  |
| `data.destination.txHash`      | `null`                    | populated                  |
| `data.destination.confirmedAt` | `null`                    | populated                  |

### Concrete payload examples

`payment_payin_completed` for a `wallet_*` account (destination tx not yet known):

```json
{
  "event_id": "f1a8c0e2-2d36-4b87-9b2f-0a7c3e91d24e",
  "type": "payment_payin_completed",
  "timestamp": "2026-05-10T14:44:55.075Z",
  "data": {
    "id": "a22a0213-9b4e-4113-adef-acdf958a84ae",
    "appId": "wallet_hellotest",
    "orderId": null,
    "status": "payment_payin_completed",
    "type": "exactIn",
    "createdAt": "2026-05-10T14:42:00.000Z",
    "updatedAt": "2026-05-10T14:44:55.000Z",
    "display": {
      "title": "Test payment",
      "currency": "USD"
    },
    "source": {
      "chainId": "8453",
      "tokenSymbol": "USDC",
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "10.00",
      "receiverAddress": "0xForwarderAddressOnBase...",
      "fee": "0.05",
      "senderAddress": "0x5772fbe7a7817ef7f586215ca8b23b8dd22c8897",
      "txHash": "0x76d360eb2ef590390e36533455da2db9f19cb2463275d93fa791cd6a3e965e5b",
      "amountReceived": "10.00",
      "confirmedAt": "2026-05-10T14:44:50.000Z"
    },
    "destination": {
      "chainId": "1500",
      "receiverAddress": "GUSERSTELLARWALLET...",
      "receiverMemo": null,
      "tokenSymbol": "USDC",
      "tokenAddress": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
      "amount": "9.95",
      "txHash": null,
      "confirmedAt": null
    }
  }
}
```

`payment_payout_completed` for the same payment (now with `destination.txHash` and `destination.confirmedAt` populated):

```json
{
  "event_id": "9d4f2e0c-7a55-4b1b-8e2a-6c1f0a5d8e30",
  "type": "payment_payout_completed",
  "timestamp": "2026-05-10T14:45:09.501Z",
  "data": {
    "id": "a22a0213-9b4e-4113-adef-acdf958a84ae",
    "appId": "wallet_hellotest",
    "orderId": null,
    "status": "payment_payout_completed",
    "type": "exactIn",
    "createdAt": "2026-05-10T14:42:00.000Z",
    "updatedAt": "2026-05-10T14:45:09.000Z",
    "display": {
      "title": "Test payment",
      "currency": "USD"
    },
    "source": {
      "chainId": "8453",
      "tokenSymbol": "USDC",
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "10.00",
      "receiverAddress": "0xForwarderAddressOnBase...",
      "fee": "0.05",
      "senderAddress": "0x5772fbe7a7817ef7f586215ca8b23b8dd22c8897",
      "txHash": "0x76d360eb2ef590390e36533455da2db9f19cb2463275d93fa791cd6a3e965e5b",
      "amountReceived": "10.00",
      "confirmedAt": "2026-05-10T14:44:50.000Z"
    },
    "destination": {
      "chainId": "1500",
      "receiverAddress": "GUSERSTELLARWALLET...",
      "tokenSymbol": "USDC",
      "tokenAddress": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
      "amount": "9.95",
      "txHash": "3YgVxWDRKtWnzKNnXR6pqghBSTCBA8axpPHjApmyNZTvhqUShohfDRpv7FaEGjh48WyrEfxrGxZxJ6E2mNJQtV8u",
      "confirmedAt": "2026-05-10T14:45:08.000Z"
    }
  }
}
```

***

## 6. Verify the Signature

**Algorithm:** HMAC-SHA256\
**Message:** `${X-Rozo-Timestamp}.${raw request body}`\
**Header:** `X-Rozo-Signature: sha256=<hex>`

Always:

1. Confirm the timestamp is within **5 minutes** of your server clock (replay protection).
2. Compute the HMAC over `timestamp + "." + raw_body` using your webhook secret.
3. Compare with **constant-time** equality. Never use `===` on signature strings.
4. Use the **raw body bytes** — do not parse and re-serialize the JSON before signing.

### Node.js / TypeScript

```typescript
import crypto from 'crypto';
import express from 'express';

const app = express();

app.post(
  '/webhooks/rozo',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const timestamp = req.headers['x-rozo-timestamp'] as string;
    const sigHeader = req.headers['x-rozo-signature'] as string;
    const rawBody = req.body.toString('utf8');
    const secret = process.env.ROZO_WEBHOOK_SECRET!;

    // 1. Replay window
    if (Math.abs(Date.now() - parseInt(timestamp, 10)) > 5 * 60 * 1000) {
      return res.status(401).json({ error: 'timestamp_expired' });
    }

    // 2. Recompute signature
    const expected = crypto
      .createHmac('sha256', secret)
      .update(`${timestamp}.${rawBody}`)
      .digest('hex');

    // 3. Constant-time compare. The header may be "sha256=<hex>" or "<hex>".
    const received = sigHeader.replace(/^sha256=/, '');
    const ok = crypto.timingSafeEqual(
      Buffer.from(expected, 'hex'),
      Buffer.from(received, 'hex'),
    );
    if (!ok) return res.status(401).json({ error: 'invalid_signature' });

    // 4. Idempotency: event_id is globally unique per delivery.
    const event = JSON.parse(rawBody);
    if (await db.alreadyProcessed(event.event_id)) {
      return res.json({ ok: true });
    }

    // 5. Business logic. event.data is the same Payment object as
    //    GET /payment-api/payments/{id}.
    if (event.type === 'payment_payout_completed') {
      await fulfillOrder(event.data.id, event.data.destination.txHash);
    }

    await db.markProcessed(event.event_id);
    res.json({ ok: true });
  },
);
```

### Python (Flask)

```python
import hmac, hashlib, time, json, os
from flask import Flask, request, jsonify

app = Flask(__name__)
SECRET = os.environ['ROZO_WEBHOOK_SECRET']

@app.post('/webhooks/rozo')
def rozo_webhook():
    ts = request.headers.get('X-Rozo-Timestamp', '')
    sig = request.headers.get('X-Rozo-Signature', '')
    raw = request.get_data(as_text=True)

    if abs(int(time.time() * 1000) - int(ts)) > 5 * 60 * 1000:
        return jsonify(error='timestamp_expired'), 401

    expected = hmac.new(SECRET.encode(), f'{ts}.{raw}'.encode(), hashlib.sha256).hexdigest()
    received = sig.removeprefix('sha256=')
    if not hmac.compare_digest(expected, received):
        return jsonify(error='invalid_signature'), 401

    event = json.loads(raw)
    if db.already_processed(event['event_id']):
        return jsonify(ok=True)

    # event['data'] is the same Payment object as GET /payment-api/payments/{id}
    if event['type'] == 'payment_payout_completed':
        fulfill_order(event['data']['id'], event['data']['destination']['txHash'])

    db.mark_processed(event['event_id'])
    return jsonify(ok=True)
```

### Go

```go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "net/http"
    "os"
    "strconv"
    "strings"
    "time"
)

func handle(w http.ResponseWriter, r *http.Request) {
    ts := r.Header.Get("X-Rozo-Timestamp")
    sigHdr := r.Header.Get("X-Rozo-Signature")
    raw, _ := io.ReadAll(r.Body)
    secret := os.Getenv("ROZO_WEBHOOK_SECRET")

    n, _ := strconv.ParseInt(ts, 10, 64)
    if abs(time.Now().UnixMilli()-n) > 5*60*1000 {
        http.Error(w, `{"error":"timestamp_expired"}`, 401)
        return
    }

    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(ts + "." + string(raw)))
    expected := hex.EncodeToString(mac.Sum(nil))
    received := strings.TrimPrefix(sigHdr, "sha256=")
    if !hmac.Equal([]byte(expected), []byte(received)) {
        http.Error(w, `{"error":"invalid_signature"}`, 401)
        return
    }

    var ev map[string]any
    json.Unmarshal(raw, &ev)
    // dedupe on ev["event_id"]; payment fields are under ev["data"].
    w.Write([]byte(`{"ok":true}`))
}

func abs(x int64) int64 { if x < 0 { return -x }; return x }
```

***

## 7. Delivery Semantics — Read This Carefully

Rozo webhooks are **at-most-once delivery**. This is a design choice, not a gap.

| Question                                 | Answer                                                                                                                                                         |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| How many times is each event delivered?  | **At most once.** No retries. Whether your server returns 2xx, 5xx, or times out, Rozo will not re-send the same event                                         |
| What happens on failure?                 | The delivery is recorded in the dashboard log as `exhausted`. **Reconciliation is on you** — call `GET /payment-api/payments/{id}` to check the current status |
| Are deliveries ordered?                  | Best-effort by event time. Do not rely on strict ordering — your handler should tolerate `payment_payout_completed` arriving before `payment_payin_completed`  |
| Are deliveries deduplicated?             | One POST per delivery, identified by `event_id`. Dedupe on `event_id` to be safe against your own proxies / framework retries                                  |
| How do I dedupe?                         | Use `event_id` as the idempotency key. It is a UUID, globally unique, and equal to the `deliveryId` shown in the dashboard delivery log                        |
| Will I get an email if a delivery fails? | No. Use the dashboard delivery log + your own monitoring                                                                                                       |

**Practical implication:** for any payment your business cares about, **don't rely on the webhook alone**. Your server should also poll `GET /payment-api/payments/{id}` (or `/payments/order/{appId}/{orderId}`) periodically until you observe a terminal status. The webhook is a fast-path optimization on top of polling — it saves you a poll, but it isn't the source of truth, and it does not fire on failure terminals.

***

## 8. Inspect Deliveries in the Dashboard

`Settings → Webhooks → Recent deliveries` shows every POST Rozo has tried to send to your URL. Each row contains:

* `deliveryId` — UUID of this delivery, **equal to the `event_id`** on the webhook body. If you store `event_id` in your handler, paste it here to find the matching record
* `paymentId` — the order it relates to
* `eventType` — the event that triggered the delivery (`payment_payin_completed` or `payment_payout_completed`)
* `statusCode` — HTTP status your endpoint returned
* `responsePreview` — first 200 chars of your response body
* `deliveredAt` — when Rozo POSTed
* `status` — `delivered` (2xx) or `exhausted` (anything else)

`attemptNumber` is always `1` (at-most-once delivery — no retries by design).

You can also fetch this programmatically:

```bash
curl -H "Authorization: Bearer $JWT" \
  "https://aozudqtlykbhzbuzalzz.supabase.co/functions/v1/merchant-api/me/webhook/deliveries?limit=50"
```

***

## 9. Test Your Endpoint

### a. Expose your localhost

```bash
ngrok http 3000
# → https://abc123.ngrok.io
```

Set `https://abc123.ngrok.io/webhooks/rozo` as your webhook URL in the dashboard.

### b. Send a signed test event

```bash
#!/usr/bin/env bash
SECRET="paste-your-64-hex-secret-here"
URL="https://abc123.ngrok.io/webhooks/rozo"
TIMESTAMP=$(date +%s%3N)

# Body must be a single canonical JSON string — sign exactly the bytes you POST.
BODY='{"event_id":"00000000-0000-0000-0000-000000000001","type":"payment_payout_completed","timestamp":"2026-05-09T00:00:00.000Z","data":{"id":"test-payment-id","appId":"merchant_hellocafe","status":"payment_payout_completed","source":{"senderAddress":"0x1111","txHash":"0xaaaa","amountReceived":"10.00"},"destination":{"txHash":"0xbbbb","confirmedAt":"2026-05-09T00:00:00.000Z"}}}'

SIG=$(printf '%s' "${TIMESTAMP}.${BODY}" \
  | openssl dgst -sha256 -hmac "$SECRET" \
  | awk '{print $2}')

curl -X POST "$URL" \
  -H "Content-Type: application/json" \
  -H "X-Rozo-Timestamp: $TIMESTAMP" \
  -H "X-Rozo-Signature: sha256=$SIG" \
  --data-binary "$BODY" -i
```

Your server should respond with `200`.

### c. Negative tests

* **Wrong signature** — replace the hex with garbage. Expect `401`.
* **Stale timestamp** — set `TIMESTAMP=$(( $(date +%s%3N) - 400000 ))`. Expect `401`.
* **Replayed (id, status)** — send the same body twice. Expect both `200`, but business logic runs only once.

### d. End-to-end with a real payment

Create a small live payment for your `appId` (any valid `POST /payment-api/payments` call where the `appId` is yours), pay it on-chain, and watch the dashboard delivery log + your own server logs.

You should observe two deliveries to your URL: one `payment_payin_completed` after the buyer's payin lands, then one `payment_payout_completed` after the destination payout confirms.

***

## 10. Identifying Which Secret Is Active

The dashboard shows the **fingerprint** of the active secret — a SHA-256 hash of the plaintext, displayed as `sha256:<64 hex chars>`. To confirm your server is using the right secret, compute the fingerprint locally:

```bash
printf '%s' "$ROZO_WEBHOOK_SECRET" | openssl dgst -sha256 | awk '{print $2}'
```

Compare with the value shown in `Settings → Webhooks`. They must match.

> **Note:** the dashboard exposes the full fingerprint as the canonical identifier — there is no short alias (e.g. `whsec_••••abcd`) by design. If you want a compact tag for your own logs, use the first 8 hex chars of the fingerprint — it's stable until the next rotation.

After rotating, the fingerprint changes; `webhookSecretRotatedAt` is also updated so you can audit when the change happened.

***

## 11. Security Checklist

* [ ] Secret stored in environment variable / secrets manager — never in code.
* [ ] Signature verified on every request **before** any side effects.
* [ ] Timestamp checked — reject anything more than 5 minutes off.
* [ ] Constant-time signature comparison.
* [ ] Raw request bytes used for signing (no re-serialize).
* [ ] Idempotent on `event_id`.
* [ ] HTTPS enforced on your endpoint (HTTP refused at registration anyway).
* [ ] Reconciliation job that polls `GET /payments/{id}` for any payment without a terminal webhook within an expected window — webhook is at-most-once and doesn't fire on failures.

***

## 12. Related

* Dashboard: [partners.rozo.ai/settings/webhooks](https://partners.rozo.ai/settings/webhooks)


# ROZO Intents Tech Design

<figure><img src="/files/ucWtmhGJUgGdhnG1lvBb" alt=""><figcaption></figcaption></figure>


# Supported Tokens and Chains

> **Note on Chain IDs for Solana and Stellar:** For non-EVM chains, the API accepts **either** the numeric chain ID **or** the lowercase chain name string:
>
> * **Solana** — `900` or `"solana"`
> * **Stellar** — `1500` or `"stellar"`
>
> Both formats are equivalent and interchangeable in all API requests.

### CCTP V2 Domain Aliases

If you're routing via Circle's CCTP V2, you can pass the CCTP domain using the **`cctp:<N>` prefix** and the API will resolve it to the canonical Rozo chain ID before validation.

<table><thead><tr><th width="140">Alias</th><th width="180">Resolves to chainId</th><th>Chain</th></tr></thead><tbody><tr><td><code>cctp:0</code></td><td><code>1</code></td><td>Ethereum</td></tr><tr><td><code>cctp:3</code></td><td><code>42161</code></td><td>Arbitrum</td></tr><tr><td><code>cctp:5</code></td><td><code>900</code></td><td>Solana</td></tr><tr><td><code>cctp:6</code></td><td><code>8453</code></td><td>Base</td></tr><tr><td><code>cctp:7</code></td><td><code>137</code></td><td>Polygon</td></tr><tr><td><code>cctp:27</code></td><td><code>1500</code></td><td>Stellar</td></tr></tbody></table>

**Rules:**

* The `cctp:` prefix is **required**. A bare integer (e.g. `27`) is treated as a literal `chainId` and will return `invalidChainId`.
* Matching is case-insensitive and whitespace-trimmed (`cctp:27`, `CCTP:27`, `Cctp:27` are all equivalent).
* `cctp:25` is **not** aliased — domain `25` belongs to Codex, which is not currently supported.
* `cctp:2` (Optimism) is not currently aliased and will be enabled separately once Optimism payout support is reviewed.

### Pay In Tokens and Chains

**USDC Support (More chains coming soon)**

<table><thead><tr><th width="118.76953125">Chain ID</th><th width="119.1953125">Chain Name</th><th width="407.73828125">USDC Token Address</th><th>Decimals</th></tr></thead><tbody><tr><td><code>1</code></td><td>Ethereum</td><td><code>0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48</code></td><td>6</td></tr><tr><td><code>42161</code></td><td>Arbitrum</td><td><code>0xaf88d065e77c8cc2239327c5edb3a432268e5831</code></td><td>6</td></tr><tr><td><code>8453</code></td><td>Base</td><td><code>0x833589fcd6edb6e08f4c7c32d4f71b54bda02913</code></td><td>6</td></tr><tr><td><code>56</code></td><td>BSC</td><td><code>0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d</code></td><td>18</td></tr><tr><td><code>137</code></td><td>Polygon</td><td><code>0x3c499c542cef5e3811e1192ce70d8cc03d5c3359</code></td><td>6</td></tr><tr><td><code>999</code></td><td>HyperEVM</td><td><code>0xb88339cb7199b77e23db6e890353e22632ba630f</code></td><td>6</td></tr><tr><td><code>900</code> or <code>solana</code></td><td>Solana</td><td><code>EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v</code></td><td>6</td></tr><tr><td><code>1500</code> or <code>stellar</code></td><td>Stellar</td><td><code>USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN</code></td><td>7</td></tr></tbody></table>

**USDT Support**

<table><thead><tr><th width="103.88671875">Chain ID</th><th width="149.35546875">Chain Name</th><th width="408.99609375">USDT Token Address</th><th>Decimals</th></tr></thead><tbody><tr><td><code>1</code></td><td>Ethereum</td><td><code>0xdac17f958d2ee523a2206206994597c13d831ec7</code></td><td>6</td></tr><tr><td><code>42161</code></td><td>Arbitrum</td><td><code>0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9</code></td><td>6</td></tr><tr><td><code>56</code></td><td>BSC</td><td><code>0x55d398326f99059ff775485246999027b3197955</code></td><td>18</td></tr><tr><td><code>137</code></td><td>Polygon</td><td><code>0xc2132d05d31c914a87c6611c10748aeb04b58e8f</code></td><td>6</td></tr><tr><td><code>900</code> or <code>solana</code></td><td>Solana</td><td><code>Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB</code></td><td>6</td></tr></tbody></table>

### Pay Out Tokens and Chains

**USDC Support (More chains coming soon)**

<table><thead><tr><th width="118.76953125">Chain ID</th><th width="119.1953125">Chain Name</th><th width="407.73828125">USDC Token Address</th><th>Decimals</th></tr></thead><tbody><tr><td><code>1</code></td><td>Ethereum</td><td><code>0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48</code></td><td>6</td></tr><tr><td><code>42161</code></td><td>Arbitrum</td><td><code>0xaf88d065e77c8cc2239327c5edb3a432268e5831</code></td><td>6</td></tr><tr><td><code>8453</code></td><td>Base</td><td><code>0x833589fcd6edb6e08f4c7c32d4f71b54bda02913</code></td><td>6</td></tr><tr><td><code>56</code></td><td>BSC</td><td><code>0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d</code></td><td>18</td></tr><tr><td><code>137</code></td><td>Polygon</td><td><code>0x3c499c542cef5e3811e1192ce70d8cc03d5c3359</code></td><td>6</td></tr><tr><td><code>999</code></td><td>HyperEVM</td><td><code>0xb88339cb7199b77e23db6e890353e22632ba630f</code></td><td>6</td></tr><tr><td><code>900</code> or <code>solana</code></td><td>Solana</td><td><code>EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v</code></td><td>6</td></tr><tr><td><code>1500</code> or <code>stellar</code></td><td>Stellar</td><td><code>USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN</code></td><td>7</td></tr></tbody></table>

**USDT Support**

<table><thead><tr><th width="103.88671875">Chain ID</th><th width="149.35546875">Chain Name</th><th width="408.99609375">USDT Token Address</th><th>Decimals</th></tr></thead><tbody><tr><td><code>1</code></td><td>Ethereum</td><td><code>0xdac17f958d2ee523a2206206994597c13d831ec7</code></td><td>6</td></tr><tr><td><code>42161</code></td><td>Arbitrum</td><td><code>0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9</code></td><td>6</td></tr><tr><td><code>56</code></td><td>BSC</td><td><code>0x55d398326f99059ff775485246999027b3197955</code></td><td>18</td></tr><tr><td><code>137</code></td><td>Polygon</td><td><code>0xc2132d05d31c914a87c6611c10748aeb04b58e8f</code></td><td>6</td></tr></tbody></table>

### EURC PayIn & PayOut

(Base and Stellar network)

<table><thead><tr><th width="109.328125">Chain ID</th><th width="119.1953125">Chain Name</th><th width="455.8515625">EURC Token Address</th><th>Decimals</th></tr></thead><tbody><tr><td><code>8453</code></td><td>Base</td><td>0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42</td><td>6</td></tr><tr><td><code>1500</code> or <code>stellar</code></td><td>Stellar</td><td>EURC:GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2</td><td>7</td></tr></tbody></table>


# Intent Based Payment & Transfer

ROZO Intent Based Payment & Transfer. Accept payments and run agentic pay-per-call flows across chains.

Accept crypto payments and transfers from users on supported chains, while you receive the token and chain you want — and let AI agents pay per call with the same Stellar USDC balance.

## Sub-products

* [**Intent Based Payment**](/products/intent-based-payment-transfer/intent-based-payment) — Accept payments and transfers from any supported chain with one integration.
* [**Agentic Payments with MPP Router**](/products/agentic-payments-mpprouter) — Pay-per-call for AI agents over 402-gated APIs, settled in Stellar USDC.


# Intent Based Payment

ROZO Intent based payments and transfers. Accept from any supported chain with one integration.

Accept crypto payments and transfers from users on supported chains, while you receive the token and chain you want.

## Why this product

* **One click UX** for end users.
* **Cross-chain abstraction** without manual bridge steps.
* **Single integration** for web and mobile flows.

## Intent Based Payment

#### Pay with Stellar with Rozo Intent Pay SDK, to a Base Merchant

{% embed url="<https://rozo-videos-cdn.s3.us-east-2.amazonaws.com/rozo-video-paystellar.MP4>" %}

### Intent Based Transfer

#### Transfer USDC to Solana (Phantom) from Base App

1. Open [ROZO Invoice](https://invoice.rozo.ai) in your wallet app (MetaMask, Base App, Bitget, Trust Wallet, etc.).
2. Scan the recipient wallet QR or paste the wallet address.
3. Select token and amount.
4. Confirm and sign.

{% embed url="<https://rozo-videos-cdn.s3.us-east-2.amazonaws.com/rozo-video-basetosol.MP4>" %}

## Related docs

* [ROZO Intent Pay SDK](/integration/rozointentpay)
* [ROZO Intent Pay API](/integration/api-doc)
* [Intent Based Bridge](/products/intent-based-bridge)
* [Intent Based Earn](/products/intent-based-earn)


# Agentic Payments (MPP Router)

Pay-per-call for AI agents. Call 402-gated HTTP APIs and settle automatically in Stellar USDC via MPP Router.

Agentic payments let AI agents spend Stellar USDC to call paid HTTP APIs — no API keys, no subscriptions, no credit card on file. The agent makes a request, the server returns `402 Payment Required` with a Stellar payment challenge, the agent signs with its wallet, retries the request, and receives the response.

ROZO's agentic payment stack runs on **Stellar mainnet** via [MPP Router](https://www.mpprouter.dev) — a Stellar-native 402 proxy that accepts Stellar USDC and forwards the request to upstream merchants.

The router fronts **90+ upstream services** across **670 endpoints**, including OpenAI, Anthropic, DeepSeek, Mistral, Gemini, Grok, Groq, OpenRouter, Perplexity, Exa, Firecrawl, Tavily, Brave, Parallel.ai, Replicate, fal, Stability AI, Deepgram, DeepL, Alchemy, Dune, Nansen, CoinGecko, QuickNode, Mapbox, Google Maps and WolframAlpha. One Stellar USDC balance pays for all of them — no per-service API key, no subscription, no card on file.

## Why Stellar for agentic payments

* **Sub-cent fees.** Each 402-gated call settles for fractions of a cent, making microtransactions practical.
* **Fast finality.** Stellar closes ledgers every \~5 seconds, so the 402 → pay → retry loop completes in one round trip.
* **Sponsored transactions.** The agent's wallet does not need XLM for fees — MPP Router sponsors the fee payer.
* **One stablecoin balance, many APIs.** You hold USDC on Stellar once and spend it across 90+ upstream services — no need to fund Base, Tron, or Solana.

## The flow

```
Agent                           Server (402-gated)
  │                                      │
  │ ───── 1. POST /endpoint ────────────▶ │
  │                                      │
  │ ◀──── 402 Payment Required ───────── │
  │       WWW-Authenticate: Payment      │
  │       request="<challenge>"          │
  │                                      │
  │  sign challenge with                 │
  │  Stellar wallet (sponsored mode)     │
  │                                      │
  │ ───── 2. POST /endpoint ────────────▶ │
  │       Authorization: Payment <cred>  │
  │                                      │
  │ ◀──── 200 OK + Payment-Receipt ──── │
```

1. First request: plain HTTP POST to the service URL.
2. Server responds `402 Payment Required` with a Stellar charge challenge (either MPP's `WWW-Authenticate: Payment` header or the x402 `Payment-Required` header).
3. Agent signs the challenge with its Stellar key. The inner transaction is a sponsored SAC transfer — the agent's account is `ALL_ZEROS` and only the auth entries are signed, so the server can fee-bump and broadcast.
4. Agent retries the request with the signed credential in the `Authorization` header.
5. Server validates the credential, broadcasts the signed transaction, and returns the upstream response plus a `Payment-Receipt` header.

## Protocols supported

MPP Router emits a single `402` response that carries **both** dialects. The inner signed XDR is identical — a single signer produces both envelopes.

| Aspect         | x402                             | MPP                                          |
| -------------- | -------------------------------- | -------------------------------------------- |
| Outer envelope | `X-Payment` header (base64 JSON) | `Authorization: Payment <credential>` header |
| Field names    | `payTo`, `asset`, `amount`       | `recipient`, `currency`, `amount`            |
| Sponsored flag | `extra.areFeesSponsored`         | `feePayer: true`                             |
| Inner XDR      | Same sponsored SAC transfer      | Same sponsored SAC transfer                  |

The MPP and x402 `payTo` addresses in a single challenge are **different** and HMAC-bound — pay the address that matches the dialect you chose. Never mix.

## Discovering services

The service catalog is live at `https://apiserver.mpprouter.dev/v1/services/catalog`. Never hardcode service paths — the catalog is the source of truth, and it carries its own `generated_at` timestamp plus a `summary` block with the current counts.

As of 2026-08-11 the catalog reports **670 endpoints from 94 upstream providers**, of which **444 are payable** and **15 are paid-verified** — meaning a real paid call was made through them and the settling Stellar transaction hash is published. Treat unverified entries as available but untested.

Representative services:

| id                 | price            | upstream                           |
| ------------------ | ---------------- | ---------------------------------- |
| `openai_*`         | dynamic          | OpenAI models                      |
| `anthropic_*`      | dynamic          | Anthropic models                   |
| `openrouter_chat`  | dynamic          | OpenRouter LLM proxy               |
| `perplexity_*`     | dynamic          | Perplexity search-grounded answers |
| `parallel_search`  | $0.01 / request  | Parallel.ai web search             |
| `exa_search`       | $0.005 / request | Exa neural search                  |
| `firecrawl_scrape` | $0.002 / request | Firecrawl HTML → markdown          |
| `tavily_*`         | dynamic          | Tavily agent search                |

Prices and availability change; the table above is illustrative and the catalog is authoritative.

## Getting started

The fastest path for Claude Code or similar AI agents is the [`stellar-agent-wallet`](https://github.com/mpprouter/stellar-agent-wallet-skill) plugin. Seven skills ship in it — `onboard`, `check-balance`, `discover`, `pay-per-call`, `send-payment`, `send-raw` and `bridge` — of which two drive the pay-per-call loop:

* `discover` — fetch and filter the MPP Router service catalog
* `pay-per-call` — execute the full 402 → sign → retry loop, handling both MPP and x402 dialects

Install the plugin, then:

```bash
# Find a service
./node_modules/.bin/tsx skills/discover/run.ts --query "web search"

# Call it — the skill pays automatically
./node_modules/.bin/tsx skills/pay-per-call/run.ts \
  "https://apiserver.mpprouter.dev/v1/services/parallel/search" \
  --body '{"query": "Summarize https://stripe.com/docs"}' \
  --method POST
```

For custom integrations, use the `@stellar/mpp` SDK on npm with `@stellar/stellar-sdk ^14.6.1` and `mppx ^0.4.11` as peer dependencies.

## Safety notes

* **Credentials are single-use.** The HMAC binding to amount, currency, and recipient is the router's defense against replay. If the retry fails, start fresh with a new 402 challenge — do not re-send the same credential.
* **Confirm above a threshold.** For mainnet calls above \~$1.00, prompt the user before signing. The `pay-per-call` skill enforces this by default (`--max-auto <usd>` to override).
* **Validate the challenge amount.** If you know the advertised price from the catalog, verify the 402 challenge amount matches before signing.
* **Mainnet only.** MPP Router does not run on Stellar testnet.

## Relationship to ROZO

MPP Router is the agentic payments rail that lets ROZO's Stellar-native wallets pay for off-chain services with the same USDC balance used for cross-chain intents. An agent holding USDC on Stellar can:

1. Bridge from another chain to Stellar USDC via a ROZO intent.
2. Call any MPP Router service and pay per request.
3. Send the result to an intent for further action (e.g. swap, settle, bridge back).

## Further reading

* MPP Router: <https://www.mpprouter.dev>
* Live service catalog: <https://apiserver.mpprouter.dev/v1/services/catalog>
* Stellar Soroban smart-account payments: [Stellar Smart Account Payments](/integration/api-doc/bridge-api/stellar-contract-payments)
* `stellar-agent-wallet` plugin skills: `onboard`, `check-balance`, `discover`, `pay-per-call`, `send-payment`, `send-raw`, `bridge`

***

*Last updated: 2026-08-11. Service counts verified against the live catalog on that date.*


# Intent Based Bridge

ROZO Intent Based Bridge. One click cross-chain bridging, wallet deposit addresses, and EURC onramp — all in one product.

ROZO's bridge stack moves stablecoins across chains in one click — and powers wallet deposit addresses and EURC onramp on top of the same intent rails.

## Sub-products

* [**One-Click Bridge**](/products/intent-based-bridge/intent-based-bridge) — One click bridging from Base to Stellar (and other supported chains).
* [**Wallet Deposit for Apps**](/products/intent-based-bridge/intent-based-deposit) — Generate unique deposit addresses for your app to receive USDC with one API call.
* [**EURC Onramp and Bridge**](/products/intent-based-bridge/eurc-onramp) — Buy EURC with bank transfer or bridge from Base.


# One-Click Bridge

ROZO Intent based bridge. One click to bridge from Base to Stellar within few seconds.

## One-Click Bridge

### Fast

It confirms within few seconds.

versus traditional bridges require waiting for 10 minutes bridging.

### One Click

It's Intent based with one click.

versus Traditional bridges require users to perform two separate actions: authorization and execution .

### **Web UI:**

{% embed url="<https://intents.rozo.ai/bridge?utm_source=docs&utm_medium=referral>" %}

**API Integration** : <https://docs.rozo.ai/integration/api-doc>

<figure><img src="/files/V5U8DqHQ47rVXdneg60k" alt="" width="375"><figcaption><p>ROZO Intents</p></figcaption></figure>

## Bridge URL Query Parameters

Pre-fill the bridge form by passing query parameters to `/bridge`.

***

### Parameters

| Parameter            | Description                  | Example                       |
| -------------------- | ---------------------------- | ----------------------------- |
| `sourceChain`        | Chain to send from           | `base`, `stellar`, `arbitrum` |
| `sourceToken`        | Token to send                | `USDC`, `EURC`                |
| `destinationChain`   | Chain to receive on          | `stellar`, `base`, `polygon`  |
| `destinationToken`   | Token to receive             | `USDC`, `EURC`                |
| `amount`             | Amount to send (ExactIn)     | `10`, `0.5`                   |
| `destinationAddress` | Pre-fill destination address | EVM or Stellar address        |

***

### Supported Chain Values

| Chain     | Accepted values            |
| --------- | -------------------------- |
| Base      | `base`, `8453`             |
| Stellar   | `stellar`, `rozo-stellar`  |
| Ethereum  | `ethereum`, `eth`, `1`     |
| Arbitrum  | `arbitrum`, `arb`, `42161` |
| BNB Chain | `bnb`, `bsc`, `56`         |
| Polygon   | `polygon`, `matic`, `137`  |
| Solana    | `solana`, `sol`            |

Chain values are **case-insensitive**. Numeric chainIds also work.

***

### Examples

**Base USDC -> Stellar USDC, amount 10**

```
/bridge?sourceChain=base&sourceToken=USDC&destinationChain=stellar&destinationToken=USDC&amount=10
```

**Base EURC -> Stellar EURC**

```
/bridge?sourceChain=base&sourceToken=EURC&destinationChain=stellar&destinationToken=EURC&amount=25
```

**Stellar -> Base with destination address pre-filled**

```
/bridge?sourceChain=stellar&destinationChain=base&destinationAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&amount=10
```

**Stellar -> Base, amount only**

```
/bridge?sourceChain=stellar&destinationChain=base&amount=50
```

**Just pre-fill amount (keep default chains)**

```
/bridge?amount=100
```

***

### Rules

* All parameters are **optional** - omitted params keep their default values.
* Unsupported chain names, unknown token symbols, zero/negative/non-numeric amounts, and addresses invalid for the destination chain are **silently ignored**.
* Only `USDC` and `EURC` are supported tokens.
* `USDC -> EURC` and `EURC -> USDC` cross-token routes are **not allowed** - the destination token will be ignored if it conflicts.
* URL params take precedence over the `?currency=EURC` shortcut.


# Wallet Deposit for Apps

Wallet Deposit for Apps. Generate a unique deposit address for your app to receive USDC with one API call.

<figure><img src="/files/EvBAnOxHRtwtQIqj3flq" alt="" width="300"><figcaption></figcaption></figure>

<figure><img src="/files/a67bSikEMwc3Z7CJNb8D" alt=""><figcaption></figcaption></figure>

{% embed url="<https://rozo.ai/deposit>" %}


# EURC Onramp and Bridge

ROZO EURC Onramp and Bridge. Buy EURC and bridge across chains in one flow.

{% embed url="<https://wallet.rozo.ai/onramp?currency=EURC>" %}

We support both bank transfer and bridging from Base.

> Need USDC instead? The same onramp and bridge flow is available at [wallet.rozo.ai/onramp?currency=USDC](https://wallet.rozo.ai/onramp?currency=USDC).

## Bank Transfer

{% embed url="<https://youtu.be/sCDMDkK6nDE>" %}

## Bridge from Base

{% embed url="<https://youtu.be/al4s66-sY4Q>" %}


# CCTP Bridge

ROZO CCTP Bridge. Native USDC bridging between Stellar and Base, Ethereum, and Solana — powered by Circle CCTP V2. Self-custody, no Rozo fee, \~30–120s.

Move native **USDC** between **Stellar** and **Base, Ethereum, and Solana** in a single flow — no wrapped tokens, no Rozo custody. The bridge is built directly on **Circle's Cross-Chain Transfer Protocol (CCTP) V2**, so every transfer is a native burn-and-mint of canonical USDC, not a liquidity-pool swap.

Try it at [bridge.rozo.ai/cctp](https://bridge.rozo.ai/cctp).

## Why it's different

* **Self-custody, always.** Rozo's operator can never move or redirect your funds. On EVM chains each transfer routes through an immutable forwarder contract whose destination is locked at creation; on Stellar you sign a single atomic transaction; on Solana the burn goes straight to Circle's TokenMessenger. A compromised Rozo key still cannot take user funds.
* **Native USDC on both ends.** Circle CCTP burns USDC on the source chain and mints canonical USDC on the destination — no wrapped or bridged representations.
* **Fast.** Built on CCTP V2 **Fast Transfer**, most bridges complete in **\~30–120 seconds**.
* **No Rozo fee (v1).** You only pay Circle's protocol fee (\~1.3 bps). The minimum transfer is **1 USDC**.
* **Stellar `G…` and `C…` addresses** are both supported as destinations.

## Supported routes

Native USDC ↔ USDC, both directions, with **Stellar** on one side of every route:

| Route              | Status |
| ------------------ | ------ |
| Base ↔ Stellar     | Beta   |
| Ethereum ↔ Stellar | Beta   |
| Solana ↔ Stellar   | Beta   |

## How it works

**To Stellar** (e.g. Base → Stellar)

1. You send USDC to a unique deposit address (EVM), or sign one transaction (Stellar/Solana source).
2. Circle attests the burn.
3. Rozo relays the mint, and native USDC lands on your Stellar address.

**From Stellar** (e.g. Stellar → Base)

1. You sign one transaction in your Stellar wallet to burn USDC.
2. Circle attests the burn.
3. Rozo relays the mint to your address on the destination chain.

Either way, Rozo only sponsors gas and relays the message — it never holds your USDC.

## Good to know

* Send only to **self-custody wallets**. Exchange deposit addresses that require a memo are not supported yet.
* When bridging **to Stellar**, make sure the destination account already has a **USDC trustline**.
* CCTP attestations are valid for \~24 hours. If one expires before the mint lands, it can be re-attested from Circle.


# Intent Based Earn

ROZO Intent Based Earn. Earn yield across chains without understanding the underlying chains or wallets. One-click access to Stellar DeFi yields.

No need to understand the underlying blockchains or manage multiple wallets.

<figure><img src="/files/k5YkPAiSoIjnlcfIC5Qa" alt=""><figcaption></figcaption></figure>

***

## FAQ <a href="#faq" id="faq"></a>

* [1. Is this high yield on some shitcoin?](#faq-1)
* [2. What is Stellar blockchain?](#faq-2)
* [3. Why can I get 16% APY?](#faq-3)
* [4. Where does the high yield come from?](#faq-4)
* [5. Why does Blend show 15%, but the ROZO says 17%?](#faq-5)
* [6. Are there risks?](#faq-6)
* [7. Can I use other wallets?](#faq-7)
* [8. How do I backup my Rozo wallet?](#faq-8)
* [9. What does Rozo wallet actually do for me?](#faq-9)
* [10. Where is my money saved?](#faq-10)
* [11. Is this non-custodial?](#faq-11)
* [12. My USDC is on Base/Solana — how can I earn Stellar yields?](#faq-12)
* [13. What are the deposit limits and confirmation time?](#faq-13)
* [14. What is ROZO?](#faq-14)

***

### 1. Is this high yield (16%+) on some shitcoin? <a href="#faq-1" id="faq-1"></a>

**No.** The yield is on [**USDC**](https://coinmarketcap.com/currencies/usd-coin/) — the regulated stablecoin issued by Circle with over **$75B** in circulation (Jan 2026).

* USDC is 1:1 backed by US dollars and US treasuries
* Issued by Circle, a regulated financial institution
* Native USDC on **Stellar**, not bridged or wrapped ([Contract Addresses](https://developers.circle.com/stablecoins/usdc-contract-addresses))

You're earning yield on one of the most trusted stablecoins in crypto, not a speculative token.

***

### 2. What is Stellar blockchain? <a href="#faq-2" id="faq-2"></a>

[**Stellar**](https://coinmarketcap.com/currencies/stellar/) is a Layer 1 blockchain focused on payments. Stellar has been operating since 2014.

* Comparable in scale and adoption to networks such as Avalanche, Sui, TON, Arbitrum, and Polygon
* Designed for fast, low-cost payments
* Native USDC is issued directly by Circle on Stellar

***

### 3. Why can I get 16% APY? Is this real? <a href="#faq-3" id="faq-3"></a>

**Yes, it's real.** This is not "TVL subsidy farming". It's a structural supply and demand result:

1. **Large XLM holder base** on Stellar network
2. **Relative scarcity of native USDC** on Stellar
3. **Strong borrowing demand** for USDC (for payments, liquidity needs)

This naturally pushes USDC lending rates higher than on chains where stablecoins are abundant.

***

### 4. Where does the high yield (12%+) come from? <a href="#faq-4" id="faq-4"></a>

ROZO Earn itself **does not generate yield**.

Current Earn yield source:

**Blend Protocol** — the #1 DeFi protocol on Stellar (Soroban)

* Stablecoin lending market
* Current APY: \~12-17%
* 7-day / 30-day historical yields in similar range
* Yields generated from **real borrowing demand**
* TVL: **\~$78M** ([DefiLlama](https://defillama.com/protocol/blend))
* Audited by **Certora** and **Code4rena**
* Active bug bounty program (up to 2M BLND)
* [Audit Reports](https://docs.blend.capital/audits-and-bug-bounties) | [Docs](https://docs.blend.capital/) | [Dune Dashboard](https://dune.com/scoffie/stellar)

Rozo does not modify or interfere with Blend's core logic.

***

### 5. Why does Blend show 15%, but ROZO says 17%? <a href="#faq-5" id="faq-5"></a>

Blend rewards lenders with **BLND tokens** in addition to the base interest rate.

We use **DeFindex** for auto-compounding — it automatically sells BLND tokens and reinvests into USDC, boosting your effective APY.

**DeFindex** provides:

* Auto-compounding (sells BLND → USDC)
* Automatic settlement
* Reduced manual operations

DeFindex uses Blend as its underlying yield source and has completed security audits.

* Audited by **OtterSec**
* [Audit Report (PDF)](https://github.com/paltalabs/defindex/blob/main/audits/2025_03_18_ottersec_defindex_audit.pdf) | [Docs](https://docs.defindex.io/)
* [Auto-compound Vault Contract](https://stellar.expert/explorer/public/contract/CBNKCU3HGFKHFOF7JTGXQCNKE3G3DXS5RDBQUKQMIIECYKXPIOUGB2S3)

***

### 6. Are there risks? <a href="#faq-6" id="faq-6"></a>

**Yes.** Using any financial application involves risk. Rozo does not promise fixed returns and does not hide risks.

**Smart Contract Risk**

Blend and DeFindex function using smart contracts. If a bug is discovered and exploited, it could result in a loss of user funds. To mitigate this:

* Blend Protocol audited by [Certora and Code4rena](https://docs.blend.capital/audits-and-bug-bounties)
* DeFindex audited by [OtterSec](https://github.com/paltalabs/defindex/blob/main/audits/2025_03_18_ottersec_defindex_audit.pdf)

**Oracle Risk**

Blend's lending pools rely on oracles to price assets accurately. If a pool's oracle stopped functioning, users could suffer a loss of funds.

**Asset Risk**

Lenders using Blend are exposed to asset risk. High volatility in assets could cause the pool to take on bad debt. Lenders could suffer asset loss if bad debt exceeds the value of assets backstopping the pool.

**Stellar Protocol Risk**

As with all decentralized ledgers, Stellar has its unique set of risks. Learn more about the [Stellar Consensus Protocol](https://developers.stellar.org/docs/learn/fundamentals/stellar-consensus-protocol).

**Cross-Chain / Bridge Risk**

We use battle-tested cross-chain infrastructure:

* [**CCTP**](https://www.circle.com/en/cross-chain-transfer-protocol) (Circle Cross-Chain Transfer Protocol) — operated by Circle, uses native mint/burn (no wrapped tokens)
* [**NEAR Intents**](https://near.org/intents) — processed **$8B+** in cross-chain volume (as of Jan 2025)
* [**Across**](https://across.to/) — intent-based bridge with optimistic verification

While these are established protocols, cross-chain operations still carry inherent risks including network delays and potential smart contract vulnerabilities.

**Interest Rate Fluctuation**

APY is variable and depends on market supply and demand. Rates can go up or down.

***

### 7. Can I use other wallets? <a href="#faq-7" id="faq-7"></a>

**Yes.** Besides [ROZO Earn](https://earn.rozo.ai), you can use any Stellar wallet to interact with the underlying protocols directly:

* [Lobstr](https://lobstr.co/)
* [Freighter](https://www.freighter.app/)
* [Beans](https://www.beansapp.com/)
* [Meru](https://meru.finance/)

**Without Rozo, you would need to:**

1. Download a new Stellar wallet app
2. Write down your seed phrase / backup keys
3. Get XLM for Stellar network fees
4. Add USDC trustline to your Stellar wallet
5. Bridge USDC from Base/Solana to Stellar
6. Connect wallet to Blend/DeFindex
7. Deposit and start earning

**With Rozo:** Start earning in 30 seconds. We handle all of the above automatically.

***

### 8. How do I backup my Rozo wallet? <a href="#faq-8" id="faq-8"></a>

Rozo wallet uses **Passkeys** for authentication. Your passkey is automatically synced and backed up by your device/platform:

* **Apple devices**: Synced via iCloud Keychain
* **Android devices**: Synced via Google Password Manager
* **Desktop browsers**: Synced via your browser's password manager (Chrome, Safari, etc.)

As long as your passkey is saved to your device's password manager, you can recover access on any device signed into the same account.

***

### 9. What does Rozo wallet actually do for me? <a href="#faq-9" id="faq-9"></a>

Rozo wallet abstracts complex operations **without changing asset ownership**:

| Without Rozo                                           | With Rozo                      |
| ------------------------------------------------------ | ------------------------------ |
| Learn about Stellar                                    | One-click deposit              |
| Create Stellar wallet                                  | Automatic cross-chain          |
| Handle Stellar asset model (Trustlines, Asset Issuers) | Automatic protocol interaction |
| Manual cross-chain bridging                            | Automatic yield earning        |
| Manual deposit to Blend Protocol                       |                                |
| Handle Blend tokens, compounding, selling              |                                |

***

### 10. Where is my money saved? Is it on your centralized server? <a href="#faq-10" id="faq-10"></a>

**No.** Your funds are **self-custodial** — stored in a smart contract wallet on Stellar, not on any Rozo server.

We use smart contract wallets powered by [OpenZeppelin Stellar Contracts](https://github.com/OpenZeppelin/stellar-contracts):

* Your wallet is a **smart contract on Stellar blockchain**
* Only you can authorize transactions via your Passkey
* Rozo cannot access, move, or freeze your funds
* All transactions are verifiable on-chain

This is the same security model used by leading smart wallets in the industry.

***

### 11. Is this non-custodial? <a href="#faq-11" id="faq-11"></a>

**Yes.**

* Users always use their own authorized signatures
* Rozo cannot unilaterally initiate transfers
* Rozo does not hold private keys or control funds

Rozo's role is an **operation coordinator**, not a **fund controller**.

***

### 12. My USDC is on Base/Solana — how can I earn Stellar yields? <a href="#faq-12" id="faq-12"></a>

This is the core abstraction layer Rozo provides. You don't need to manually bridge anything.

**How it works:**

1. You deposit USDC from Base, Solana, or any supported chain
2. Rozo automatically bridges your USDC to Stellar using **CCTP** (Circle Cross-Chain Transfer Protocol)
3. Your USDC is deposited into Blend Protocol via DeFindex
4. You start earning yield immediately

**How does the bridging work?**

We use [**CCTP**](https://www.circle.com/en/cross-chain-transfer-protocol) — Circle's official cross-chain protocol for USDC:

* USDC is **burned** on the source chain (e.g., Base)
* USDC is **minted** on the destination chain (Stellar)
* This is **native USDC** on both sides — no wrapped tokens, no bridge risk from third-party liquidity pools
* Circle guarantees 1:1 redemption

**When you withdraw:**

The reverse happens — USDC is burned on Stellar and minted on your destination chain.

**Supported infrastructure:**

* **CCTP** (Circle Cross-Chain Transfer Protocol) — primary bridge for USDC
* **Across** — additional cross-chain support
* **NEAR Intents** — intent-based routing

All bridging is handled automatically. You just deposit and withdraw.

***

### 13. What are the deposit limits and confirmation time? <a href="#faq-13" id="faq-13"></a>

**During Beta:**

|                             | Amount         |
| --------------------------- | -------------- |
| Fast earn suggested minimum | **0.5 USDC**   |
| Fast earn suggested maximum | **3,000 USDC** |

**Confirmation time:**

* Most deposits confirm within **\~30 seconds**
* Larger amounts ($5000+) still work, and may take a few more minutes
* We're continuously improving speed and capacity (faster processing coming mid-Jan 2025)

***

### 14. What is ROZO? <a href="#faq-14" id="faq-14"></a>

**ROZO promotes economic freedom.**

We build infrastructure that lets anyone earn yield and make payments — without needing to understand complex blockchain systems.

**Long-term focus:**

* **PayFi** (Pay to Earn) — Assets used for payments can continuously generate yield

**Supported by:**

* [Stellar Community Fund](https://x.com/StellarOrg/status/2001332873937457395)
* [Base Grants](https://www.coinbase.com/developer-platform/discover/launches/summer-builder-grants)
* [Draper Founder Residency](https://x.com/draper_u/status/1940908242412183926)
* [Circle Alliance](https://partners.circle.com/partner/rozo)

**Try ROZO:**

* [ROZO Earn](https://earn.rozo.ai) — Earn yield on USDC
* [ROZO Bridge](https://intents.rozo.ai/bridge?utm_source=docs\&utm_medium=referral) — Intent based bridge

***

## Start now

<https://earn.rozo.ai>


# Mobile App

## ROZO Wallet

Current mobile app is invite only. Please contact us if you want to earn in our mobile app.

### iOS App

{% embed url="<https://apps.apple.com/app/id6754191938>" %}

### Android

{% embed url="<https://play.google.com/store/apps/details?id=com.rozoapp>" %}

## ROZO POS (Merchant App)

The merchant-side app for accepting multi-chain payments at the point of sale.

### iOS App

{% embed url="<https://apps.apple.com/app/id6776572346>" %}

### Android

{% embed url="<https://play.google.com/store/apps/details?id=ai.rozo.merchant>" %}

### Support and Contact Us

{% embed url="<https://app.gitbook.com/o/aDtyMnbBPEzS5Bo1I0D3/s/huTMWMzO32xbGDjNWeOa/~/edit/~/changes/28/mobile-app/support>" %}


# dApp Integration

Integrate **Pay with Rozo Wallet** into your dApp using the `window.rozo` provider for gasless USDC transfers on Stellar.

When your dApp is opened inside the Rozo Wallet mobile app, the `window.rozo` provider is automatically injected into the WebView, giving users:

* **Gasless USDC payments** — fees sponsored by OpenZeppelin Relayer
* **Biometric authentication** — Face ID / Touch ID
* **No wallet setup** — the user's Rozo Wallet is ready to go

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                     Your dApp (WebView)                      │
│                                                              │
│  1. Detect window.rozo                                       │
│  2. Build USDC transfer transaction                          │
│  3. Call window.rozo.signAuthEntry()                         │
└──────────────────────────┬──────────────────────────────────┘
                           │ postMessage
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                  Rozo Wallet App (Native)                    │
│                                                              │
│  4. Show confirmation modal → User approves                  │
│  5. Biometric authentication → Face ID / Touch ID            │
│  6. Sign with Passkey → Submit to Relayer (gasless!)        │
│  7. Return { hash, status, signedAuthEntry }                 │
└─────────────────────────────────────────────────────────────┘
```

## Quick Start

### 1. Detect Rozo Wallet

```javascript
// Check if window.rozo exists
if (!window.rozo) {
  // Wait for rozo:ready event
  await new Promise((resolve) => {
    window.addEventListener('rozo:ready', resolve, { once: true });
    setTimeout(resolve, 3000);
  });
}

if (!window.rozo) {
  console.log('Not in Rozo Wallet');
  return;
}

// Check connection
const { isConnected } = await window.rozo.isConnected();
if (!isConnected) {
  console.log('Wallet not connected');
  return;
}
```

### 2. Get Wallet Info

```javascript
// Get wallet address
const { address } = await window.rozo.getAddress();
console.log('Wallet:', address); // "CXXX...XXX"

// Get network details
const { network, sorobanRpcUrl, networkPassphrase } =
  await window.rozo.getNetworkDetails();
console.log('Network:', network); // "PUBLIC" or "TESTNET"

// Get balance
const { balance } = await window.rozo.getBalance();
console.log('Balance:', balance); // "10000000" (1.0 USDC in stroops)
```

### 3. Build USDC Transfer Transaction

```javascript
import {
  Account,
  Address,
  Contract,
  nativeToScVal,
  TransactionBuilder,
} from '@stellar/stellar-sdk';
import { Server } from '@stellar/stellar-sdk/rpc';

// USDC contract addresses
const USDC_CONTRACTS = {
  PUBLIC: 'CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75',
  TESTNET: 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA',
};

// Setup
const server = new Server(sorobanRpcUrl);
const usdcContractId = network === 'PUBLIC'
  ? USDC_CONTRACTS.PUBLIC
  : USDC_CONTRACTS.TESTNET;
const usdcContract = new Contract(usdcContractId);

// Convert amount to stroops (7 decimals)
// "10.50" → 105000000n
function toStroops(amount) {
  const [whole, decimal = ''] = amount.split('.');
  const paddedDecimal = decimal.padEnd(7, '0').slice(0, 7);
  return BigInt(whole + paddedDecimal);
}

const amountStroops = toStroops('10.50');

// Build transfer operation
const hostFunction = usdcContract.call(
  'transfer',
  new Address(fromAddress).toScVal(),
  new Address(toAddress).toScVal(),
  nativeToScVal(amountStroops, { type: 'i128' })
);

// Create dummy source (Relayer will replace this)
const dummySource = new Account(
  'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF',
  '0'
);

// Build transaction
const tx = new TransactionBuilder(dummySource, {
  fee: '100',
  networkPassphrase,
})
  .addOperation(hostFunction)
  .setTimeout(30)
  .build();
```

### 4. Simulate to Get Auth Entries

```javascript
const simulation = await server.simulateTransaction(tx);

if ('error' in simulation) {
  throw new Error(`Simulation failed: ${simulation.error}`);
}

// Extract auth entries
const authEntries = simulation.result?.auth || [];
if (authEntries.length === 0) {
  throw new Error('No auth entries found');
}

// Convert to XDR (base64)
const authEntryXdr =
  typeof authEntries[0] === 'string'
    ? authEntries[0]
    : authEntries[0].toXDR('base64');
```

### 5. Extract Host Function XDR

```javascript
const txXdr = tx.toEnvelope().v1().tx();
const opXdr = txXdr.operations()[0].body().invokeHostFunctionOp();
const funcXdr = opXdr.hostFunction().toXDR('base64');
```

### 6. Sign and Submit via `window.rozo`

```javascript
const result = await window.rozo.signAuthEntry(authEntryXdr, {
  func: funcXdr,           // Host function XDR (required for submit: true)
  submit: true,            // Submit via Relayer (gasless!)
  message: 'Transfer 10.50 USDC',  // User-facing description
});

console.log('Transaction hash:', result.hash);
console.log('Status:', result.status);
console.log('Signed auth entry:', result.signedAuthEntry);
```

## What Happens in the Wallet

When you call `window.rozo.signAuthEntry()`:

1. **Confirmation Modal** appears showing the description, recipient, amount, and network
2. **User clicks "Confirm"** — or cancels (throws `"User rejected the signing request"`)
3. **Biometric Authentication** — Face ID / Touch ID (cannot be bypassed)
4. **Sign with Passkey** — signs in secure enclave, private key never exposed
5. **Submit to Relayer** (when `submit: true`) — OpenZeppelin Relayer sponsors gas fees
6. **Return Result**:

```javascript
{
  signedAuthEntry: "AAAABg...",  // Signed XDR
  hash: "a1b2c3...",             // Transaction hash
  status: "PENDING"              // Status
}
```

## Complete Example: Vanilla JavaScript

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pay with Rozo Wallet</title>
</head>
<body>
  <div id="app">
    <h1>Coffee Shop</h1>
    <p id="status">Loading...</p>
    <div id="payment-form" style="display: none;">
      <p>Balance: <span id="balance">-</span> USDC</p>
      <input type="number" id="amount" value="5.00" step="0.01" />
      <button id="pay-btn">Pay with Rozo Wallet</button>
    </div>
  </div>

  <script type="module">
    import {
      Account,
      Address,
      Contract,
      nativeToScVal,
      TransactionBuilder,
    } from 'https://cdn.jsdelivr.net/npm/@stellar/stellar-sdk/+esm';
    import { Server } from 'https://cdn.jsdelivr.net/npm/@stellar/stellar-sdk/rpc/+esm';

    const MERCHANT_ADDRESS = 'GDQP2KPQGKIHYJGXNUIYOMHARUARCA7DJT5FO2FFOOUJ3UBSIB3GN5QA';

    const USDC_CONTRACTS = {
      PUBLIC: 'CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75',
      TESTNET: 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA',
    };

    const statusEl = document.getElementById('status');
    const paymentFormEl = document.getElementById('payment-form');
    const balanceEl = document.getElementById('balance');
    const amountInput = document.getElementById('amount');
    const payBtn = document.getElementById('pay-btn');

    function showStatus(message) {
      statusEl.textContent = message;
    }

    function fromStroops(stroops) {
      const amount = BigInt(stroops);
      const whole = amount / BigInt(10_000_000);
      const decimal = amount % BigInt(10_000_000);
      const decimalStr = decimal.toString().padStart(7, '0');
      return `${whole}.${decimalStr}`.replace(/\.?0+$/, '');
    }

    function toStroops(amount) {
      const [whole, decimal = ''] = amount.split('.');
      const paddedDecimal = decimal.padEnd(7, '0').slice(0, 7);
      return BigInt(whole + paddedDecimal);
    }

    async function transferUSDC(toAddress, amount) {
      showStatus('Preparing transaction...');

      try {
        const { address: fromAddress } = await window.rozo.getAddress();
        const { sorobanRpcUrl, networkPassphrase, network } =
          await window.rozo.getNetworkDetails();

        const server = new Server(sorobanRpcUrl);
        const usdcContractId =
          network === 'PUBLIC' ? USDC_CONTRACTS.PUBLIC : USDC_CONTRACTS.TESTNET;
        const usdcContract = new Contract(usdcContractId);

        const amountStroops = toStroops(amount);

        const hostFunction = usdcContract.call(
          'transfer',
          new Address(fromAddress).toScVal(),
          new Address(toAddress).toScVal(),
          nativeToScVal(amountStroops, { type: 'i128' })
        );

        const dummySource = new Account(
          'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF',
          '0'
        );

        const tx = new TransactionBuilder(dummySource, {
          fee: '100',
          networkPassphrase,
        })
          .addOperation(hostFunction)
          .setTimeout(30)
          .build();

        showStatus('Simulating transaction...');
        const simulation = await server.simulateTransaction(tx);

        if ('error' in simulation) {
          throw new Error(`Simulation failed: ${simulation.error}`);
        }

        const authEntries = simulation.result?.auth || [];
        if (authEntries.length === 0) {
          throw new Error('No auth entries found');
        }

        const authEntryXdr =
          typeof authEntries[0] === 'string'
            ? authEntries[0]
            : authEntries[0].toXDR('base64');

        const txXdr = tx.toEnvelope().v1().tx();
        const opXdr = txXdr.operations()[0].body().invokeHostFunctionOp();
        const funcXdr = opXdr.hostFunction().toXDR('base64');

        showStatus('Waiting for confirmation...');
        const result = await window.rozo.signAuthEntry(authEntryXdr, {
          func: funcXdr,
          submit: true,
          message: `Transfer ${amount} USDC`,
        });

        if (!result.hash) {
          throw new Error('Transaction submission failed');
        }

        showStatus(`Payment successful! Tx: ${result.hash.slice(0, 8)}...`);
        await refreshBalance();
        return result;
      } catch (error) {
        if (error.message.includes('User rejected')) {
          showStatus('Payment cancelled by user');
        } else {
          showStatus(`Error: ${error.message}`);
        }
        throw error;
      }
    }

    async function refreshBalance() {
      if (!window.rozo) return;
      try {
        const { balance } = await window.rozo.getBalance();
        balanceEl.textContent = fromStroops(balance);
      } catch (error) {
        console.error('Failed to get balance:', error);
      }
    }

    async function init() {
      if (!window.rozo) {
        await new Promise((resolve) => {
          window.addEventListener('rozo:ready', resolve, { once: true });
          setTimeout(resolve, 3000);
        });
      }

      if (!window.rozo) {
        showStatus('Please open this page in the Rozo Wallet app');
        return;
      }

      const { isConnected } = await window.rozo.isConnected();
      if (!isConnected) {
        showStatus('Wallet not connected');
        return;
      }

      const { address } = await window.rozo.getAddress();
      await refreshBalance();

      showStatus('Ready to accept payments');
      paymentFormEl.style.display = 'block';

      payBtn.onclick = async () => {
        const amount = amountInput.value;
        if (!amount || parseFloat(amount) <= 0) {
          alert('Please enter a valid amount');
          return;
        }

        const balance = parseFloat(balanceEl.textContent);
        if (balance < parseFloat(amount)) {
          alert(`Insufficient balance. You have ${balance} USDC.`);
          return;
        }

        payBtn.disabled = true;
        try {
          await transferUSDC(MERCHANT_ADDRESS, amount);
        } finally {
          payBtn.disabled = false;
        }
      };
    }

    init().catch(console.error);
  </script>
</body>
</html>
```

## React Example

```tsx
import { useEffect, useState } from 'react';
import {
  Account,
  Address,
  Contract,
  nativeToScVal,
  TransactionBuilder,
} from '@stellar/stellar-sdk';
import { Server } from '@stellar/stellar-sdk/rpc';

const USDC_CONTRACTS = {
  PUBLIC: 'CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75',
  TESTNET: 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA',
};

function toStroops(amount: string): bigint {
  const [whole, decimal = ''] = amount.split('.');
  const paddedDecimal = decimal.padEnd(7, '0').slice(0, 7);
  return BigInt(whole + paddedDecimal);
}

function PaymentButton() {
  const [isConnected, setIsConnected] = useState(false);
  const [address, setAddress] = useState<string | null>(null);
  const [balance, setBalance] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    async function init() {
      if (!window.rozo) return;

      const { isConnected } = await window.rozo.isConnected();
      if (!isConnected) return;

      const { address } = await window.rozo.getAddress();
      const { balance } = await window.rozo.getBalance();

      setIsConnected(true);
      setAddress(address);
      setBalance(balance);
    }

    init();
  }, []);

  async function handlePay() {
    if (!window.rozo) return;

    setIsLoading(true);

    try {
      const { sorobanRpcUrl, networkPassphrase, network } =
        await window.rozo.getNetworkDetails();
      const { address: fromAddress } = await window.rozo.getAddress();

      const server = new Server(sorobanRpcUrl);
      const usdcContractId =
        network === 'PUBLIC' ? USDC_CONTRACTS.PUBLIC : USDC_CONTRACTS.TESTNET;
      const usdcContract = new Contract(usdcContractId);

      const toAddress = 'GDQP2KPQGKIHYJGXNUIYOMHARUARCA7DJT5FO2FFOOUJ3UBSIB3GN5QA';
      const amount = '10.00';
      const amountStroops = toStroops(amount);

      const hostFunction = usdcContract.call(
        'transfer',
        new Address(fromAddress).toScVal(),
        new Address(toAddress).toScVal(),
        nativeToScVal(amountStroops, { type: 'i128' })
      );

      const dummySource = new Account(
        'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF',
        '0'
      );

      const tx = new TransactionBuilder(dummySource, {
        fee: '100',
        networkPassphrase,
      })
        .addOperation(hostFunction)
        .setTimeout(30)
        .build();

      const simulation = await server.simulateTransaction(tx);

      if ('error' in simulation) {
        throw new Error(`Simulation failed: ${simulation.error}`);
      }

      const authEntries = simulation.result?.auth || [];
      const authEntryXdr =
        typeof authEntries[0] === 'string'
          ? authEntries[0]
          : authEntries[0].toXDR('base64');

      const txXdr = tx.toEnvelope().v1().tx();
      const opXdr = txXdr.operations()[0].body().invokeHostFunctionOp();
      const funcXdr = opXdr.hostFunction().toXDR('base64');

      const result = await window.rozo.signAuthEntry(authEntryXdr, {
        func: funcXdr,
        submit: true,
        message: `Transfer ${amount} USDC`,
      });

      alert(`Success! Transaction: ${result.hash}`);

      const { balance: newBalance } = await window.rozo.getBalance();
      setBalance(newBalance);
    } catch (error) {
      if (!error.message.includes('User rejected')) {
        alert(`Error: ${error.message}`);
      }
    } finally {
      setIsLoading(false);
    }
  }

  if (!isConnected) {
    return <div>Open in Rozo Wallet to pay</div>;
  }

  return (
    <div>
      <p>Wallet: {address}</p>
      <p>Balance: {balance ? (BigInt(balance) / 10_000_000n).toString() : '-'} USDC</p>
      <button onClick={handlePay} disabled={isLoading}>
        {isLoading ? 'Processing...' : 'Pay $10 USDC'}
      </button>
    </div>
  );
}

export default PaymentButton;
```

## Key Points

### Always Use `submit: true`

```javascript
await window.rozo.signAuthEntry(authEntryXdr, {
  func: funcXdr,
  submit: true,  // This makes it gasless!
});
```

This submits via OpenZeppelin Relayer, making transactions gasless for users.

### Amount Conversion

USDC on Stellar has **7 decimals**:

| Amount     | Stroops     |
| ---------- | ----------- |
| 1.00 USDC  | 10,000,000  |
| 10.50 USDC | 105,000,000 |

```javascript
function toStroops(amount) {
  const [whole, decimal = ''] = amount.split('.');
  const paddedDecimal = decimal.padEnd(7, '0').slice(0, 7);
  return BigInt(whole + paddedDecimal);
}
```

### Error Handling

```javascript
try {
  await window.rozo.signAuthEntry(...);
} catch (error) {
  if (error.message.includes('User rejected')) {
    // User cancelled — don't show error
  } else if (error.message.includes('Insufficient balance')) {
    // Not enough USDC
  } else {
    console.error(error);
  }
}
```

### Network Detection

```javascript
const { network } = await window.rozo.getNetworkDetails();

const usdcAddress = network === 'PUBLIC'
  ? 'CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75'  // Mainnet
  : 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA'; // Testnet
```

## Testing

1. **Install Rozo Wallet** mobile app (iOS/Android)
2. **Start your dev server** with HTTPS:

   ```bash
   npx ngrok http 3000
   ```
3. **Open your app** in Rozo Wallet's Explore tab
4. **Test on testnet** first before mainnet


# Support

Welcome to Rozo App Support! This page provides resources and information to help you get the most out of the Rozo app.

## Contact Information

We're here to help! Reach out to us through any of the following channels:

* **Email**: <hi@rozo.ai>
* **Contact Form**: [Submit a Support Request](https://forms.gle/iRcUc44a3Pzj2KpP7)
* **X (Twitter)**: [@rozoai](https://x.com/rozoai)
* **Discord**: [Join our Discord Community](https://discord.com/invite/EfWejgTbuU)
* **GitHub**: [Rozo on GitHub](https://github.com/rozoai)

***

## Guides & Tutorials

### Getting Started with Rozo App

#### 1. Registration

**Step 1: Download the App**

* Download Rozo from the App Store
* Open the app on your device

**Step 2: Sign in with Privy**

* Tap "Sign In" on the welcome screen
* Enter your email to receive a one-time passcode (OTP)
* Enter the 6-digit OTP to verify your email

**Step 3: Create or Connect Your Wallet**

* After verifying, Rozo will provision a secure embedded wallet via Privy automatically
* Optionally, connect an external wallet (e.g., using WalletConnect) if you prefer

| Welcome                                 | Enter Email                                 | Enter OTP                                 |
| --------------------------------------- | ------------------------------------------- | ----------------------------------------- |
| ![Welcome](/files/fD6ywtFt1pTIKVDXXOD9) | ![Enter Email](/files/upTH4kAROOeLP84jCraU) | ![Enter OTP](/files/WTN7CCDVuXY0XXaIwX3N) |

***

#### 2. Depositing Funds

**Step 1: Navigate to Balance**

* Open the app and tap "Balance" on the home screen
* Select the asset you want to deposit

**Step 2: Tap Receive**

* Tap the "Receive" button for the selected asset

**Step 3: Enter Deposit Amount**

* Input the amount you intend to deposit
* Review any displayed minimums or limits

**Step 4: Scan QR or Use Payment Link**

* Scan the displayed QR code with your external wallet
* Or tap/copy the "Payment" link to open in a compatible wallet

**Step 5: Choose Deposit Method**

* Select your preferred method (e.g., on-chain transfer, supported rails)
* Follow the prompts in your wallet to complete the payment

**Confirmation**

* You'll receive a notification when funds arrive
* Check your updated balance in the wallet section

| Balance                                 | Enter Amount                                 | QR / Payment Link                                  | Choose Method                                 |
| --------------------------------------- | -------------------------------------------- | -------------------------------------------------- | --------------------------------------------- |
| ![Balance](/files/MjX0IWuYdqCjBIcdnTZd) | ![Enter Amount](/files/eZpRnwqdDQ6EuIUk3E1r) | ![QR or Payment Link](/files/VDry9dbRLAz7H4eJ8NpU) | ![Choose Method](/files/gCAlwPzJOcNi3EUckrwZ) |

**Important Notes:**

* Always double-check the network before sending
* Minimum deposit amounts may apply
* Network fees are paid separately from your deposit amount

***

#### 3. Withdrawing Funds

**Step 1: Open Balance**

* Go to the "Balance" page in the app

**Step 2: Tap Withdraw**

* Tap the "Withdraw" button for the asset you want to send out

**Step 3: Enter Withdrawal Details**

* Input the destination wallet address
* Enter the amount to withdraw
* Confirm to submit the withdrawal

| Balance                                 | Withdraw Modal                           |
| --------------------------------------- | ---------------------------------------- |
| ![Balance](/files/cE0ed0ZmHOldUAZ9Igke) | ![Withdraw](/files/ABsPEoVw5CzFb4KuIfK6) |

**Security Tips:**

* Always verify the destination address
* Start with a small test transaction for new addresses
* Ensure the destination address supports the Base network before sending

***

#### 4. Making Payments

**Step 1: Initiate Payment**

* Tap "Pay" or scan a merchant QR code
* Enter the payment amount

**Step 2: Select Payment Method**

* Choose which stablecoin to use
* The app automatically finds the best route
* Review the exchange rate and fees

**Step 3: Confirm Transaction**

* Review payment details
* Tap "Confirm Payment"
* Transaction completes in seconds

***

## Frequently Asked Questions (FAQs)

### 1. What is the Rozo App?

Rozo is a stablecoin wallet and payment app that makes it easy to self manage your assets. With Rozo, you can seamlessly manage multiple stablecoins across different blockchains without worrying about complex technical details.

### 2. How do I register for a Rozo account?

Getting started is quick with Privy (Email + OTP only):

* Download the app from the App Store
* Tap "Continue", enter your email, and request a one-time passcode (OTP)
* Enter the 6-digit OTP to verify your email
* Your secure embedded wallet is created automatically via Privy
* Optionally connect an external wallet and enable biometric lock

Takes about 1–2 minutes.

### 3. What stablecoins can I use?

Rozo currently supports popular stablecoins including:

* USDC (USD Coin).
* Additional tokens coming soon

You can deposit these on multiple networks including Base and Stellar

### 4. How long does a deposit take?

Deposit times vary by network:

* **Fast networks** (Base, Stellar): 2-5 seconds

You'll receive a notification as soon as your deposit is confirmed.

### 5. Are there any fees for deposits?

Rozo does not charge fees for deposits. However, you'll need to pay the blockchain network fee (gas fee) when sending from your external wallet. These fees vary by network and are paid to blockchain validators, not to Rozo.

### 6. How do I withdraw funds from my Rozo wallet?

To withdraw:

1. Tap "Withdraw" in the app
2. Select the asset and network
3. Enter the destination address and amount
4. Review and confirm the transaction
5. Complete 2FA verification if enabled

Withdrawals typically process within 2-5 seconds. ncel" if the option is available.

### 7. What should I do if I sent funds to the wrong address?

Unfortunately, blockchain transactions are irreversible. If you sent funds to an incorrect address:

* Double-check the transaction on a blockchain explorer
* If you control the destination wallet, you can access the funds there
* If it was sent to someone else's address, contact them directly
* Contact our support team for guidance

Always verify addresses before confirming transactions!

### 8. Can I use Rozo in my country?

Rozo is available in most countries worldwide. However, we cannot provide services to residents of certain restricted jurisdictions due to regulatory requirements.

### 15. How do I contact customer support?

You can reach us through:

* **Support Form**: [forms.gle/iRcUc44a3Pzj2KpP7](https://forms.gle/iRcUc44a3Pzj2KpP7)
* **Email**: <hi@rozo.ai>

We typically respond within 24 hours (often much faster!).

***

## Still Need Help?

If you couldn't find the answer to your question, please don't hesitate to reach out:

**Submit a Support Ticket**: <https://forms.gle/iRcUc44a3Pzj2KpP7>

**Email Us**: <hi@rozo.ai>

**Join Our Community**: Connect with other Rozo users on [Discord](https://discord.com/invite/EfWejgTbuU) or follow us on [X](https://twitter.com/rozoai)


# Privacy Policy

**Privacy Policy**

This privacy policy applies to the ROZO app (hereby referred to as "Application") for mobile devices that was created by ROZO LLC (hereby referred to as "Service Provider") as a Free service. This service is intended for use "AS IS".

**Information Collection and Use**

The Application collects information when you download and use it. This information may include information such as

* Your device's Internet Protocol address (e.g. IP address)
* The pages of the Application that you visit, the time and date of your visit, the time spent on those pages
* The time spent on the Application
* The operating system you use on your mobile device

The Application does not gather precise information about the location of your mobile device.

The Application collects your device's location, which helps the Service Provider determine your approximate geographical location and make use of in below ways:

* Geolocation Services: The Service Provider utilizes location data to provide features such as personalized content, relevant recommendations, and location-based services.
* Analytics and Improvements: Aggregated and anonymized location data helps the Service Provider to analyze user behavior, identify trends, and improve the overall performance and functionality of the Application.
* Third-Party Services: Periodically, the Service Provider may transmit anonymized location data to external services. These services assist them in enhancing the Application and optimizing their offerings.

The Service Provider may use the information you provided to contact you from time to time to provide you with important information, required notices and marketing promotions.

For a better experience, while using the Application, the Service Provider may require you to provide us with certain personally identifiable information, including but not limited to email. The information that the Service Provider request will be retained by them and used as described in this privacy policy.

**Third Party Access**

Only aggregated, anonymized data is periodically transmitted to external services to aid the Service Provider in improving the Application and their service. The Service Provider may share your information with third parties in the ways that are described in this privacy statement.

Please note that the Application utilizes third-party services that have their own Privacy Policy about handling data. Below are the links to the Privacy Policy of the third-party service providers used by the Application:

* [Google Play Services](https://www.google.com/policies/privacy/)
* [Expo](https://expo.io/privacy)

The Service Provider may disclose User Provided and Automatically Collected Information:

* as required by law, such as to comply with a subpoena, or similar legal process;
* when they believe in good faith that disclosure is necessary to protect their rights, protect your safety or the safety of others, investigate fraud, or respond to a government request;
* with their trusted services providers who work on their behalf, do not have an independent use of the information we disclose to them, and have agreed to adhere to the rules set forth in this privacy statement.

**Analytics**

We use PostHog and other analytics tools to understand product usage, improve reliability, troubleshoot issues, and measure feature adoption. Analytics data may include device information, application events, wallet addresses, transaction metadata, and account identifiers. We do not collect or store private keys, seed phrases, or other wallet secrets through analytics services.

**Opt-Out Rights**

You can stop all collection of information by the Application easily by uninstalling it. You may use the standard uninstall processes as may be available as part of your mobile device or via the mobile application marketplace or network.

**Data Retention Policy**

The Service Provider will retain User Provided data for as long as you use the Application and for a reasonable time thereafter. If you'd like them to delete User Provided Data that you have provided via the Application, please contact them at <hi@rozo.ai> and they will respond in a reasonable time.

**Children**

The Service Provider does not use the Application to knowingly solicit data from or market to children under the age of 13.

The Application does not address anyone under the age of 13. The Service Provider does not knowingly collect personally identifiable information from children under 13 years of age. In the case the Service Provider discover that a child under 13 has provided personal information, the Service Provider will immediately delete this from their servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact the Service Provider (<hi@rozo.ai>) so that they will be able to take the necessary actions.

**Security**

The Service Provider is concerned about safeguarding the confidentiality of your information. The Service Provider provides physical, electronic, and procedural safeguards to protect information the Service Provider processes and maintains.

**Changes**

This Privacy Policy may be updated from time to time for any reason. The Service Provider will notify you of any changes to the Privacy Policy by updating this page with the new Privacy Policy. You are advised to consult this Privacy Policy regularly for any changes, as continued use is deemed approval of all changes.

This privacy policy is effective as of 2025-10-09

**Your Consent**

By using the Application, you are consenting to the processing of your information as set forth in this Privacy Policy now and as amended by us.

**Contact Us**

If you have any questions regarding privacy while using the Application, or have questions about the practices, please contact the Service Provider via email at <hi@rozo.ai>.


# Base Sepolia → Stellar Testnet

End-to-end testnet bridges from **Base Sepolia USDC** to **Stellar testnet USDC**, covering both Stellar destination types:

* **G-address** (classic Stellar account) — payout via Horizon `Operation.payment`
* **C-address** (Soroban smart-contract account, e.g. smart wallets, AMMs) — payout via the **Stellar Asset Contract (SAC)** `transfer(from, to, i128)` over Soroban RPC

Both flows hit the public testnet API at:

```
https://intentapitestnet.rozo.ai
```

No auth header required. USDC only. Amount range 0.01–1.00. Order TTL 10 min.

***

## Scripts

| Script                                                                                                               | Destination      | Default amount |
| -------------------------------------------------------------------------------------------------------------------- | ---------------- | -------------- |
| [`testnet-base-to-stellar.sh`](https://github.com/RozoAI/rozo-docs/blob/main/testnet/testnet-base-to-stellar.sh)     | G-address (`G…`) | `0.01`         |
| [`testnet-base-to-stellar-c.sh`](https://github.com/RozoAI/rozo-docs/blob/main/testnet/testnet-base-to-stellar-c.sh) | C-address (`C…`) | `0.02`         |

Both scripts:

1. `POST /payments` with `type=exactIn`, `source.chainId=84532` (Base Sepolia), `destination.chainId=1500` (Stellar testnet).
2. Print the returned `source.receiverAddress` (Rozo hub) and `source.amount` (may be auto-incremented by $0.01 for collision avoidance).
3. Either pause for a manual deposit, or — with `AUTO=1` — auto-broadcast using the test keys in `.env.dev`.
4. Poll `GET /payments/{id}` until `payment_completed`, `payment_bounced`, or `payment_expired`.

***

## Quick start

```bash
# Manual deposit (you send the USDC yourself from any wallet)
bash testnet/testnet-base-to-stellar.sh 0.01
bash testnet/testnet-base-to-stellar-c.sh 0.02

# Auto-deposit using TEST_EVM_PAYER_PRIVATE_KEY
AUTO=1 bash testnet/testnet-base-to-stellar.sh 0.01
AUTO=1 bash testnet/testnet-base-to-stellar-c.sh 0.02
```

Override the C-address destination:

```bash
AUTO=1 bash testnet/testnet-base-to-stellar-c.sh 0.02 CCYO2DDE3ZWBGGRTGOWJOV4KO2HLXHCT7WN7TH7BOYUGQMXTGKWU4IGV
```

***

## `.env.dev` requirements

| Variable                                                | Purpose                           |
| ------------------------------------------------------- | --------------------------------- |
| `TEST_EVM_PAYER_PRIVATE_KEY` + `TEST_EVM_PAYER_ADDRESS` | Source-side signer (Base Sepolia) |
| `TEST_STELLAR_RECEIVER_ADDRESS`                         | Default G-address destination     |
| `TEST_STELLAR_C_RECEIVER_ADDRESS`                       | Default C-address destination     |

Generate fresh test keys: `npx tsx scripts/gen-hub-keys.ts --test-wallets`. Fund payer at the [Circle USDC faucet](https://faucet.circle.com/) and [Alchemy Base Sepolia faucet](https://www.alchemy.com/faucets/base-sepolia).

***

## What's different about the C-address payout

A Stellar **C-address** is a Soroban contract identifier, not a classic account. Three differences vs. the G-path:

1. **No `memo_text`.** Soroban contract calls have no memo field. Any memo passed in the order is dropped with a log warning.
2. **SAC contract call, not `payment` op.** The hub invokes `transfer(from, to, i128)` on the auto-deployed Stellar Asset Contract for USDC. The contract id is derived deterministically from `(asset, networkPassphrase)`.
3. **Submitted via Soroban RPC**, not Horizon. Status is polled with `getTransaction(hash)` and the `meta` returned is Protocol-23 `TransactionMeta v4` — make sure any client decoding it uses `@stellar/stellar-sdk` ≥ 15 (older SDKs throw `Bad union switch: 4` on the decode).

The order schema is identical for both — clients pass `destination.receiverAddress` as either a `G…` or a `C…` and the API routes correctly.

***

## Verified end-to-end run

A run of `testnet-base-to-stellar-c.sh 0.02` against `CCYO2DDE3ZWBGGRTGOWJOV4KO2HLXHCT7WN7TH7BOYUGQMXTGKWU4IGV`:

```
Payment id : pay_ewHL66ke5XdqP7GLZ8xnkVPh
Send TO    : 0x4843B70de3Aa77CFbfDA0BAf468e3C4f32B1FA34
Send AMOUNT: 0.03 USDC (Base Sepolia)   ← 0.02 + $0.01 collision step
Dest C-addr: CCYO2DDE3ZWBGGRTGOWJOV4KO2HLXHCT7WN7TH7BOYUGQMXTGKWU4IGV
…
[1] status = payment_unpaid
[2] status = payment_completed
```

| Leg                                         | Tx hash                                                              | Confirmation                                                     |
| ------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Base Sepolia payin (USDC → hub)             | `0x92d4ed6ab6270c9d7e0db4b251ccd2c8aef41f9be311678491e6cf6caa199237` | block 41065985, success                                          |
| Stellar payout (SAC `transfer` → C-address) | `e7eedc04fb5605550a0e5c8c98ad310fe26f93b3bee5c6bf65af24d6477c4572`   | ledger 2378571, SUCCESS, 0.0300000 USDC credited to the contract |

End-to-end time: \~10 seconds.

***

## Status transitions

```
payment_unpaid → payment_payin_completed → payment_completed   (happy path)
payment_unpaid → payment_expired                                 (TTL)
              ↘ payment_bounced                                   (payout error)
```

`payment_bounced` with `errorMessage: "Bad union switch: 4"` on a C-address destination means the deployed payout function is on an outdated `@stellar/stellar-sdk` (Protocol 23 `TransactionMeta` decoding broke pre-15.0). Bump the SDK and redeploy.


# Contracts & Audits

### **Smart Contracts** <a href="#smart-contracts" id="smart-contracts"></a>

ROZO is built on smart contracts, Solver and Relayer model. \[[Github](https://github.com/RozoAI/rozo-intents-contracts)]

### **Audits** <a href="#audits" id="audits"></a>

ROZO smart contracts have been audited by [Hacken](https://hacken.io/), a leading Web3 security auditing firm.

* **Hacken × Rozo (SDF) Audit — March 2026**: [hacken.io/audits/rozo/sca-rozo-sdf-audit-mar2026](https://hacken.io/audits/rozo/sca-rozo-sdf-audit-mar2026/)

<br>

Please contact us for Bug bounties.


# Media Kit

### About

ROZO : Visa for stablecoins

ROZO Intents: Intent-based USDC transfers between Base and Stellar.

### Media Kit

<https://drive.google.com/file/d/1BrJGbZykvFc9FxWyeHkioTGs5xGSCUsK/view?usp=sharing>

### Our Logo

Here is the logo rozo-square-black.png on our Twitter. Attached the image URL on CDN.

<https://imagedelivery.net/AKLvTMvIg6yc9W08fHl1Tg/fdfef53e-91c2-4abc-aec0-6902a26d6c00/80x>

{% file src="/files/zbPJ6QHeYw2YGdWRFb0D" %}

{% file src="/files/DWjeuY0oBwKSQwESR1Az" %}

{% file src="/files/27oTWBURl9UgVUfoKo8O" %}

{% file src="/files/BuHGJil4eXRJixgdeQHI" %}

{% file src="/files/8wdMQj4Cw7QNtbk6tlsu" %}

{% file src="/files/CS6oI9PGuj3m52XaOlBy" %}

{% file src="/files/S8lf7BvJKr8tUlPIIR1i" %}

{% file src="/files/X6aSZvRC5jjcHv9ypY9u" %}

{% file src="/files/5NKumtkxoY4h9UtVAJik" %}

{% file src="/files/9RssSmwDnl8tkEoM6jiV" %}

{% file src="/files/TYps0QtvCncZ24NTYw31" %}

{% file src="/files/zESLEyxCSPyfMbsD9VGo" %}

{% file src="/files/VeLHXMLsT4Ze8JKC4rIJ" %}

{% file src="/files/nDAjitrDCUxGaBIFGn9D" %}


# Design Prompt

### Brand Personality

Minimal, trustworthy, technical, fast, precise, premium, calm.

**Avoid**: playful, cartoonish, futuristic for the sake of futuristic, crypto casino style, loud Web3 aesthetics.

**Do not use**: gradients, neon effects, colorful illustrations, 3D objects, glassmorphism, decorative blobs, cyberpunk lighting, excessive shadows, emoji-style icons, or playful UI elements.

The design should feel **calm, precise, premium, and infrastructure-grade**.

***

### Color System

Monochrome-first. No brand color. Status colors are functional only — never decorative.

| Token            | Value                            | Usage                            |
| ---------------- | -------------------------------- | -------------------------------- |
| Background       | `#FFFFFF` or `#FAFAFA`           | Page background                  |
| Primary text     | `#0A0A0A`                        | Headlines, body                  |
| Secondary text   | `#525252`                        | Metadata, captions               |
| Border           | `#E5E5E5`                        | Hairline dividers, inputs, cards |
| Muted surface    | `#F5F5F5`                        | Quiet backgrounds                |
| Primary button   | Black bg, white text             | Single primary action per screen |
| Secondary button | White bg, black text, 1px border | Supporting actions               |

**Status colors** (use sparingly):

* Success — small green indicator only
* Warning / error — minimal amber / red text or icon only

🔑 Usage rules\
• Primary CTA → black\
• Hover → slight elevation / subtle outline (not color)\
• Focus → thin black ring\
• Red → only for irreversible danger\
• No blue anywhere by default\
• Do not use status colors as decoration

***

### Typography

* Font family: **Inter** (all weights, all UI surfaces)
* Fallback stack: `Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`
* Load from [Google Fonts](https://fonts.google.com/specimen/Inter) or [rsms.me/inter](https://rsms.me/inter/)
* Clean sans-serif only — avoid decorative fonts
* Headlines confident but not oversized
* Use **font weight** for emphasis instead of color

**Hierarchy**

| Level   | Desktop | Mobile  |
| ------- | ------- | ------- |
| H1      | 48–64px | 36–44px |
| H2      | 32–40px | —       |
| H3      | 20–24px | —       |
| Body    | 16px    | —       |
| Caption | 12–14px | —       |

***

### Layout & Spacing

* Generous whitespace
* 12-column grid on desktop
* Max content width **1120–1200px**
* Spacing scale: **4, 8, 12, 16, 24, 32, 48, 64**
* Avoid crowded dashboards
* Every screen has **one clear primary action**

***

### Components

**Buttons** — rectangular with subtle radius, no glow, no gradient.

**Cards** — white or very light gray, 1px border, minimal or no shadow.

**Inputs** — clean border, clear focus state, no heavy background.

**Tables** — simple rows, thin dividers, strong alignment.

**Modals** — centered, white, clear action hierarchy.

**Icons** — outline style, monochrome, 16–20px, functional only.

**Border radius**

* Small components: 6–8px
* Cards: 12–16px
* Avoid overly rounded pill shapes (except for small labels)

***

### CSS Tokens

```css
{
  // Base colors
  background: '#FFFFFF',
  foreground: '#0A0A0A',

  // Surfaces (almost flat)
  card: '#FCFCFC',
  'card-foreground': '#0A0A0A',

  // Primary action (monochrome, confident)
  primary: '#0A0A0A',            // black / near-black button
  'primary-foreground': '#FFFFFF',

  // Secondary (quiet surface)
  secondary: '#F5F5F5',
  'secondary-foreground': '#0A0A0A',

  // Muted / metadata
  muted: '#F5F5F5',
  'muted-foreground': '#525252',

  // Accent (same as primary — no brand color)
  accent: '#0A0A0A',
  'accent-foreground': '#FFFFFF',

  // Success (subtle, not celebratory)
  success: '#15803D',
  'success-foreground': '#FFFFFF',

  // Destructive (only when truly dangerous)
  destructive: '#B91C1C',
  'destructive-foreground': '#FFFFFF',

  // Warning (rare)
  warning: '#A16207',
  'warning-foreground': '#0A0A0A',

  // Borders & focus (hairline, neutral)
  border: '#E5E5E5',
  input: '#E5E5E5',
  ring: '#0A0A0A',
}
```


# Design System

ROZO's design system — monochrome, infrastructure-grade, built for stablecoin payments. Light theme only; no brand color by design. Design tokens plus the rules for using them.

> **Version:** `alpha`. Tokens may change without notice during alpha. Hex values are sRGB.

## Overview

ROZO is a design system for building consistent, infrastructure-grade interfaces for stablecoin payments. The aesthetic is monochrome, hairline, and quiet: plenty of whitespace, near-black on white, and structure carried by 1px borders rather than shadows. The test for any choice is "would this feel right on a Bloomberg terminal or a switch's admin page?" Prioritize readability, density that breathes, and restraint. Use weight, space, and contrast — not color — to signal importance.

This is the single theme. There is no dark theme and no brand color by design; black (`#0a0a0a`) is the accent. Light-only is a deliberate alpha constraint: financial data needs high-fidelity contrast that is hard to guarantee across a dark palette without a dedicated second token set, so dark mode is deferred to a later version rather than half-done now.

## Colors

ROZO is monochrome-first. There is no blue, no purple, no brand hue — if you reach for a color to make something feel important, that is the wrong choice; use weight, space, or contrast instead.

### Core neutrals

| Token                 | Value     | Usage                                                              |
| --------------------- | --------- | ------------------------------------------------------------------ |
| `background`          | `#ffffff` | Flat white page background                                         |
| `background-alt`      | `#fafafa` | Cooler page chrome behind cards                                    |
| `foreground`          | `#0a0a0a` | Primary text, icons, primary action                                |
| `card`                | `#fcfcfc` | Surface that must read as separate from the page                   |
| `muted` / `secondary` | `#f5f5f5` | Quiet fills, hover tints                                           |
| `muted-foreground`    | `#525252` | Secondary text: metadata, captions, column headers                 |
| `border`              | `#e5e5e5` | The 1px hairline that carries structure \~95% of the time          |
| `border-strong`       | `#d4d4d4` | A firmer edge — hover borders and inputs that need more definition |

### Action & accent

`primary` and `accent` share the value `#0a0a0a` / `#ffffff` **on purpose**, but mean different things: `primary` is the fill of the one primary action on a screen (the black button); `accent` is the near-black used to emphasize a word, a number, or an active state. Keep them as separate tokens so either can evolve without disturbing the other.

`input` (`#e5e5e5`) is intentionally the same value as `border`. It is a distinct token so form-control edges can be tuned independently from structural hairlines later — reference `input` on form elements, `border` on everything structural.

`ring` (`#0a0a0a`) is the focus color (see [Focus](#focus)).

### Neutral scale

The `neutral-*` scale runs light to near-black for tables, dividers, and opaque fills.

| `50`      | `100`     | `200`     | `300`     | `400`     | `500`     | `600`     | `700`     | `800`     | `900`     |
| --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
| `#fafafa` | `#f5f5f5` | `#e5e5e5` | `#d4d4d4` | `#a3a3a3` | `#737373` | `#525252` | `#404040` | `#262626` | `#0a0a0a` |

### Status colors

Status colors are **functional only** and rare: `success` green (`#15803d`) for settled, `warning` amber (`#a16207`) for action required, `destructive` red (`#b91c1c`) for hard failure or destructive confirmation. They appear as a single dot, a thin underline, or a word — **never as a fill behind a card or chip**. No gradients, no glows, no neon, no glass. If a surface must read as separate from the page, give it a `1px solid #e5e5e5` border and a `#fcfcfc` background.

### Contrast

All text/background pairs hold WCAG AA (≥ 4.5:1 for body, ≥ 3:1 for large/UI text) on white.

| Foreground                         | On                     | Ratio  | Level                     |
| ---------------------------------- | ---------------------- | ------ | ------------------------- |
| `foreground` `#0a0a0a`             | `background` `#ffffff` | 20.1:1 | AAA                       |
| `muted-foreground` `#525252`       | `background` `#ffffff` | 7.6:1  | AAA                       |
| `muted-foreground` `#525252`       | `muted` `#f5f5f5`      | 6.9:1  | AA                        |
| `success` `#15803d`                | `background` `#ffffff` | 4.8:1  | AA                        |
| `warning` `#a16207`                | `background` `#ffffff` | 4.8:1  | AA                        |
| `destructive` `#b91c1c`            | `background` `#ffffff` | 5.9:1  | AA                        |
| `neutral-400` `#a3a3a3` (disabled) | `background` `#ffffff` | 2.5:1  | UI only — never body text |

## Typography

Inter sets the entire UI and prose stack; JetBrains Mono is reserved for code, transaction hashes, addresses, and amount strings on developer surfaces. We never reach for a display font, a serif, or a script. For CJK, fall back to the system CJK stack (`"PingFang SC", "Microsoft YaHei", "Hiragino Sans", sans-serif`) — Inter does not cover Han glyphs.

| Style           | Font           | Size / line         | Weight | Tracking           |
| --------------- | -------------- | ------------------- | ------ | ------------------ |
| `display`       | Inter          | 64 / 70             | 600    | -1.28px            |
| `h1`            | Inter          | 48 / 53             | 600    | -0.72px            |
| `h2`            | Inter          | 32 / 40             | 600    | -0.48px            |
| `h3`            | Inter          | 24 / 30             | 600    | -0.12px            |
| `h4`            | Inter          | 20 / 25             | 600    | -0.1px             |
| `body`          | Inter          | 16 / 26             | 400    | 0                  |
| `small`         | Inter          | 14 / 20             | 400    | 0                  |
| `caption`       | Inter          | 12 / 16             | 400    | 0                  |
| `eyebrow`       | Inter          | 12 / 16             | 500    | +0.96px, uppercase |
| `mono-14/13/12` | JetBrains Mono | 14·13·12 / 20·20·16 | 400    | 0                  |

* **Headings** (`display` 64 → `h4` 20) title pages and sections; tracking tightens as size grows, and headings cap at 64px — confident, not oversized.
* **Body** (16px) and **small** (14px) cover most text. Letter-spacing is `0` below headings.
* **Eyebrow** is the 12px uppercase, `+0.96px`-tracked, medium-weight label used over KPIs and section headers.
* **Mono** pairs JetBrains Mono for hashes, addresses, and amounts; always use tabular figures (`font-variant-numeric: tabular-nums`) where numbers must align — in tables and on dashboards.

Weight does the work of color: a 600-weight word inside a 400-weight paragraph is how we emphasize. Keep to two weights per view (400 body + 600 emphasis); reserve 700/800 for marketing surfaces.

## Layout

Spacing follows a 4px scale: 4, 8, 12, 16, 24, 32, 48, 64px. Keep a three-step rhythm: 8px inside a group, 16px between groups, 32–48px between sections. Do not invent 18, 22, 40. Cards pad 24px (16px compact, 32px hero). Center content in a 12-column grid, 24px gutters, 1120–1200px max. Dashboards breathe — a table row is 56px tall, not 36. Sidebars are 240–280px fixed with a hairline right border; top bars are 56–64px fixed with a hairline bottom border. One primary action per screen; everything else is secondary or tertiary.

**Breakpoints.** `sm 640` · `md 768` · `lg 1024` · `xl 1280` · `2xl 1536`. The fixed sidebar collapses to an off-canvas drawer below `lg`; the 12-column grid drops to 8 columns at `md` and a single column below `sm`. Tables become horizontally scrollable rather than reflowing below `md`.

**Stacking (`z-index`).** Use named layers, never ad-hoc values: `base 0` · `dropdown 1000` · `sticky 1100` · `overlay 1200` · `modal 1300` · `toast 1400` · `tooltip 1500`.

## Elevation & depth

Hierarchy comes from tonal surfaces and 1px borders first, so shadows stay essentially absent. The default elevation of a card is its border.

* Raised cards: `none` (border only)
* Floating menu / popover (ceiling): `0 4px 12px rgba(10,10,10,0.06), 0 0 0 1px rgba(10,10,10,0.04)`
* Hairline lift: `0 1px 0 0 rgba(10,10,10,0.04)`

Never wrap a card in a soft drop shadow; if it needs to separate, use a border.

## Motion

Fast, neutral, almost invisible. `120ms` for hover, `180ms` for state changes, `260ms` for layered transitions like a modal entering. Easing is `cubic-bezier(0.2, 0, 0, 1)`. No bounce, no spring, no overshoot, no parallax, no scroll-jacking, no decorative looping. Loading is a 1px progress bar or a thin spinner — never a shimmer skeleton. Honor `prefers-reduced-motion` by dropping nonessential motion.

## Shapes

Radii stay tight and consistent within a view: `6px` for everyday controls (buttons, inputs, search), `8px` available for secondary surfaces, `12–16px` for cards, `9999px` reserved for small status chips and avatars only. Never wrap a whole card in a pill, and never mix rounded and sharp corners in one view.

| Token | `xs` | `sm` | `md` | `lg` | `xl` | `pill` |
| ----- | ---- | ---- | ---- | ---- | ---- | ------ |
| Value | 4px  | 6px  | 8px  | 12px | 16px | 9999px |

## Components

The component tokens give ready-to-use values drawn from this theme.

* **Primary button**: solid `#0a0a0a` fill, white label, 6px radius, 40px tall — the single most important action on a view. Hover darkens to `#262626`.
* **Secondary button**: white fill, `#e5e5e5` border, gains a `#0a0a0a` border on hover.
* **Ghost button**: transparent, tints with `muted` on hover, for low-emphasis actions.
* **Destructive button**: solid `#b91c1c` fill, white text, for hard/destructive actions.
* Sizes are `sm` 32px (12px horizontal padding) and `lg` 48px (20px padding) around the default 40px medium (16px padding). Icon-only buttons are square at each height (32/40/48px).
* **Input**: white fill, `#e5e5e5` border, 6px radius, 40px tall; on focus, the border goes to `#0a0a0a` and the standard focus ring is applied (see [Focus](#focus)).
* **Card**: white / `#fcfcfc`, 1px `#e5e5e5` border, 12–16px radius, no shadow, 24–32px padding. Title top-left, actions top-right, content below. Interactive (clickable) cards shift their background to `background-alt` on hover — no shadow, no scale.
* **Badge / status**: the status is carried by a `6px × 6px` dot with `border-radius: 50%` (a circle — unrelated to the `rounded` scale); the text stays neutral. No fill, no colored border.
* **Table**: header row on `muted` (`#f5f5f5`), 1px `border` row separators, 12–16px cell padding, 56px row height. Numbers and status align right (JetBrains Mono, tabular figures); text and dates align left.

### Focus

There is exactly one focus treatment. Use the `shadows.focus` token everywhere — a thin black ring on a white gap:

```css
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #0a0a0a; /* shadows.focus */
}
```

It is always visible at `:focus-visible` and never removed. (Earlier drafts described a soft `rgba(10,10,10,0.08)` ring on inputs — that is deprecated; the double-ring above is the only focus style.)

Disabled uses a `muted` fill, `neutral-400` text, and a not-allowed cursor.

## Using tokens

These tokens are meant to be copied straight into a project. Two ways to consume them:

### CSS custom properties

Name variables `--rozo-<group>-<name>`:

```css
:root {
  /* color */
  --rozo-color-background: #ffffff;
  --rozo-color-foreground: #0a0a0a;
  --rozo-color-muted: #f5f5f5;
  --rozo-color-muted-foreground: #525252;
  --rozo-color-border: #e5e5e5;
  --rozo-color-success: #15803d;
  --rozo-color-warning: #a16207;
  --rozo-color-destructive: #b91c1c;

  /* radius / spacing / motion */
  --rozo-radius-sm: 6px;
  --rozo-radius-lg: 12px;
  --rozo-space-4: 16px;
  --rozo-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --rozo-shadow-focus: 0 0 0 2px #ffffff, 0 0 0 4px #0a0a0a;
}
```

### Tailwind config

```js
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        background: "#ffffff",
        foreground: "#0a0a0a",
        muted: { DEFAULT: "#f5f5f5", foreground: "#525252" },
        border: "#e5e5e5",
        success: "#15803d",
        warning: "#a16207",
        destructive: "#b91c1c",
      },
      borderRadius: { sm: "6px", md: "8px", lg: "12px", xl: "16px" },
      fontFamily: {
        sans: ["Inter", "system-ui", "sans-serif"],
        mono: ["JetBrains Mono", "ui-monospace", "monospace"],
      },
      transitionTimingFunction: { standard: "cubic-bezier(0.2,0,0,1)" },
    },
  },
}
```

### Component example

A primary button, built only from tokens:

```html
<button class="h-10 px-4 rounded-[6px] bg-foreground text-white text-sm font-medium
               transition-colors duration-[120ms] hover:bg-neutral-800
               focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_#fff,0_0_0_4px_#0a0a0a]">
  Send payment
</button>
```

## Voice & content

The voice is declarative, technical, and quiet. ROZO does not sell; it states. Copy reads like an SDK reference or a hardware spec sheet, not a Web3 landing page.

* **Sentence case** for everything — buttons, headings, menu items, titles. ("Create invoice," not "Create Invoice.") Product names and acronyms keep canonical case: ROZO, USDC, EVM, SDK, POS. Short status chips may use Title Case ("Settled", "Pending").
* Name actions with a verb and a noun (`Send payment`, `Delete member`), never `Confirm`, `OK`, or a bare verb.
* Write errors as what happened plus where: `Transfer failed. Insufficient gas on Base.`
* Status reads as fact, not celebration: `Settled · $48.20 USDC`, never "🎉 Payment successful!".
* Empty states point to the first action: `Connect a wallet to continue.`
* In-progress states use the present participle with an ellipsis: `Settling…`, `Routing…`.
* Prefer the vocabulary of rails: **settle, route, intent, rail, chain, net, clear**. Avoid **seamless, revolutionary, next-gen, empower, unleash**, and crypto slang (`gm`, `wagmi`, `ape in`).
* Use numerals (`3 chains`), the en dash for ranges (`5–8 sec`), ISO currency codes (USDC, USDT). No `please`, no marketing superlatives.
* **No emoji.** Anywhere. No exclamation points except in destructive confirmations ("This cannot be undone.").

**Formatting.** Amounts: `$48.20 USDC` (two decimals, ISO code after, space before). Addresses: truncate as first-6 + last-4 (`0x49CD…eEe0`). Dates: ISO-like `2026-06-21` or `Jun 21, 2026`; times in 24h with timezone. Percentages: one decimal (`9.8% APY`).

## Do's and don'ts

* Use the gray scale to rank information: `foreground` for primary text, `muted-foreground` for secondary, `neutral-400` for disabled.
* Emphasize with weight (600), not color or highlight.
* Let a 1px border be the default elevation; reserve shadow for floating menus only.
* Hold WCAG AA contrast (4.5:1 for body text).
* Show the focus ring on every interactive element at `:focus-visible`; never remove an outline without a visible replacement.
* Use the typography and spacing tokens instead of hand-setting size, line height, weight, or odd pixel gaps.
* Don't reach for a brand color, a gradient, a glow, or a glass effect — there is no brand color.
* Don't signal status with a fill; the dot, underline, or word carries it.
* Don't mix rounded and sharp corners, or more than two font weights, in one view.
* Don't use decorative icons or emoji; every icon has a job (status, action, or category).


# Contact us

We're here to help! Reach out to us through any of the following channels:

* **Email**: <hi@rozo.ai>
* **Contact Form**: [Submit a Support Request](https://forms.gle/iRcUc44a3Pzj2KpP7)
* **X (Twitter)**: [@rozoai](https://x.com/rozoai)
* **Discord**: [Join our Discord Community](https://discord.com/invite/EfWejgTbuU)
* **GitHub**: [Rozo on GitHub](https://github.com/rozoai)
* **Website**: <https://www.rozo.ai/>

***


# Terms of service

**Last Updated:** \[2025-08-28]

## 1. Acceptance of Terms

By accessing or using our service, you agree to be bound by these Terms of Service and all applicable laws and regulations. If you do not agree with any of these terms, you are prohibited from using or accessing this service.

## 2. Description of Service

Rozo provides a comprehensive stablecoin payment infrastructure platform that serves as "the Visa for stablecoins." Our service includes payment processing, settlement, merchant tools, and related financial technology services for stablecoin transactions. We reserve the right to modify, suspend, or discontinue the service at any time without notice.

## 3. User Accounts

### 3.1 Registration

* You must provide accurate and complete information when creating an account
* You are responsible for maintaining the security of your account credentials
* You must notify us immediately of any unauthorized use of your account

### 3.2 Account Responsibilities

* You are solely responsible for all activities under your account
* You must not share your account credentials with others
* You must keep your account information up to date

## 4. Acceptable Use

### 4.1 Permitted Uses

You may use our service for lawful purposes only and in accordance with these Terms.

### 4.2 Prohibited Uses

You may not use our service:

* For any unlawful purpose or to solicit others to perform unlawful acts
* To violate any international, federal, provincial, or state regulations, rules, laws, or local ordinances
* For money laundering, terrorist financing, or any other illegal financial activities
* To process transactions involving sanctioned entities or prohibited jurisdictions
* To facilitate gambling, adult content, or other restricted merchant categories without proper licensing
* To transmit or procure the sending of any advertising or promotional material without our prior written consent
* To impersonate or attempt to impersonate the company, a company employee, another user, or any other person or entity
* In any way that infringes upon the rights of others, or in any way is illegal, threatening, fraudulent, or harmful
* To engage in any conduct that could harm the stability or security of stablecoin networks or blockchain infrastructure
* To engage in any other conduct that restricts or inhibits anyone's use or enjoyment of the service

## 5. Content

### 5.1 User Content

* You retain ownership of content you submit to our service
* By submitting content, you grant us a worldwide, non-exclusive, royalty-free license to use, reproduce, modify, and distribute your content in connection with the service
* You are solely responsible for your content and must ensure it doesn't violate any laws or third-party rights

### 5.2 Our Content

* All content provided by us is protected by intellectual property laws
* You may not reproduce, distribute, modify, or create derivative works of our content without permission

## 6. Privacy

Your privacy is important to us. Please review our Privacy Policy, which also governs your use of the service.

## 7. Termination

### 7.1 Termination by You

You may terminate your account at any time by contacting us or using the account deletion feature.

### 7.2 Termination by Us

We may terminate or suspend your account immediately, without prior notice or liability, for any reason, including without limitation if you breach the Terms.

## 8. Disclaimers

### 8.1 Service Availability

* The service is provided "as is" and "as available"
* We do not guarantee uninterrupted or error-free service
* We reserve the right to modify or discontinue the service at any time

### 8.2 No Warranties

We disclaim all warranties, express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, and non-infringement.

## 9. Financial Services and Stablecoin Risks

### 9.1 Financial Risk Disclosure

* Stablecoin transactions involve inherent risks including but not limited to blockchain network congestion, smart contract vulnerabilities, and market volatility
* While stablecoins are designed to maintain stable value, they are not guaranteed and may experience price fluctuations
* Rozo acts as a technology provider and payment processor but does not issue, control, or guarantee any stablecoins
* You acknowledge that blockchain transactions are generally irreversible

### 9.2 Regulatory Compliance

* You are responsible for compliance with all applicable laws and regulations in your jurisdiction
* Stablecoin regulations may vary by country and are subject to change
* You must ensure you have the necessary licenses and approvals to conduct stablecoin transactions in your business

## 10. Limitation of Liability

In no event shall we be liable for any indirect, incidental, special, consequential, or punitive damages, including without limitation, loss of profits, data, use, goodwill, digital assets, or other intangible losses, resulting from your use of the service. This includes but is not limited to losses from stablecoin price fluctuations, blockchain network failures, or third-party stablecoin issuer actions.

## 11. Indemnification

You agree to defend, indemnify, and hold harmless the company and its licensee and licensors, and their employees, contractors, agents, officers and directors, from and against any and all claims, damages, obligations, losses, liabilities, costs or debt, and expenses (including but not limited to attorney's fees).

## 12. Governing Law

These Terms shall be interpreted and governed by the laws of \[Your Jurisdiction], without regard to its conflict of law provisions.

## 13. Changes to Terms

We reserve the right to modify or replace these Terms at any time. If a revision is material, we will try to provide at least 30 days' notice prior to any new terms taking effect.

## 14. Contact Information

If you have any questions about these Terms of Service, please contact us at:

**Email:** \[<hi@rozo.ai>]

*These Terms of Service are effective as of \[2025-08-28] and was last updated on \[2025-08-28].*


# Privacy Policy

**Last Updated:** \[2025-08-28]

## 1. Introduction

This Privacy Policy describes how Rozo ("the Visa for stablecoins") collects, uses, and protects your information when you use our stablecoin payment infrastructure platform and related services. By using our service, you agree to the collection and use of information in accordance with this policy.

## 2. Information We Collect

### 2.1 Personal Information

We may collect personally identifiable information that you provide to us, including but not limited to:

* Name, email address, and business contact information
* Account credentials and authentication data
* Business profile and merchant information
* KYC (Know Your Customer) documentation and identity verification data
* Financial institution details and banking information
* Tax identification numbers and business registration details
* Communication preferences

### 2.2 Transaction and Usage Data

We automatically collect certain information when you use our service:

* Transaction data including amounts, timestamps, and wallet addresses
* Stablecoin network activity and blockchain interaction data
* Payment processing metrics and settlement information
* API usage patterns and integration data
* IP address and device information
* Browser type and version
* Pages visited and time spent
* Referring website addresses
* Operating system information

### 2.3 Cookies and Tracking Technologies

We use cookies and similar tracking technologies to:

* Remember your preferences
* Analyze service usage
* Improve user experience
* Provide personalized content

## 3. How We Use Your Information

We use the collected information for various purposes:

* **Service Provision:** To provide and maintain our stablecoin payment infrastructure
* **Transaction Processing:** To facilitate, settle, and record stablecoin transactions
* **Compliance:** To meet KYC, AML, and financial regulations requirements
* **Risk Management:** To assess and mitigate financial and operational risks
* **Communication:** To contact you with updates, security alerts, and support messages
* **Improvement:** To analyze usage patterns and improve our service
* **Legal Compliance:** To comply with applicable laws and financial regulations
* **Security:** To detect and prevent fraud, money laundering, and abuse

## 4. Information Sharing and Disclosure

### 4.1 Third-Party Service Providers

We may share your information with trusted third-party service providers who assist us in:

* Hosting and maintaining our service
* Processing stablecoin transactions and settlements
* Blockchain network operations and monitoring
* Identity verification and KYC compliance services
* Financial institution partnerships and banking services
* Risk assessment and fraud prevention
* Analyzing service usage
* Providing customer support

### 4.2 Legal Requirements and Financial Regulations

We may disclose your information if required to do so by law or in response to:

* Legal process or court orders
* Government requests and regulatory inquiries
* Financial intelligence units and anti-money laundering authorities
* Sanctions screening and compliance obligations
* Protection of our rights and safety
* Investigation of potential violations or suspicious activities
* Blockchain network governance and compliance requirements

### 4.3 Business Transfers

In the event of a merger, acquisition, or sale of assets, your information may be transferred to the acquiring entity.

### 4.4 Analytics

We use PostHog and other analytics tools to understand product usage, improve reliability, troubleshoot issues, and measure feature adoption. Analytics data may include device information, application events, wallet addresses, transaction metadata, and account identifiers. We do not collect or store private keys, seed phrases, or other wallet secrets through analytics services.

## 5. Data Security

We implement appropriate technical and organizational measures to protect your personal information against:

* Unauthorized access
* Alteration or destruction
* Accidental loss
* Unlawful processing

However, no method of transmission over the internet or electronic storage is 100% secure.

## 6. Data Retention

We retain your personal information only for as long as necessary to:

* Fulfill the purposes outlined in this Privacy Policy
* Comply with financial regulations and recordkeeping requirements
* Maintain transaction histories for audit and compliance purposes
* Resolve disputes
* Enforce our agreements

Note: Financial and transaction data may be retained for extended periods as required by applicable financial regulations, typically 5-7 years.

## 7. Your Rights and Choices

### 7.1 Access and Correction

You have the right to:

* Access your personal information
* Correct inaccurate or incomplete data
* Request updates to your information

### 7.2 Data Portability

You may request a copy of your personal information in a structured, machine-readable format.

### 7.3 Deletion

You may request deletion of your personal information, subject to certain legal limitations.

### 7.4 Opt-Out

You can opt out of:

* Marketing communications
* Non-essential cookies
* Data processing for certain purposes

## 8. International Data Transfers

Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place to protect your information during such transfers.

## 9. Financial Services and Regulatory Compliance

### 9.1 Financial Data Protection

As a financial technology service provider, we implement enhanced security measures for financial and transaction data, including:

* Encryption of sensitive financial information
* Segregated storage of transaction data
* Regular security audits and compliance assessments
* Multi-factor authentication for account access

### 9.2 Blockchain and Stablecoin Considerations

* Blockchain transactions are publicly visible and immutable
* While we protect your personal information, blockchain addresses and transaction amounts may be publicly accessible
* Stablecoin issuers may have their own privacy policies governing their tokens

## 10. Children's Privacy

Our service is not intended for children under 18 years of age due to the financial nature of our services. We do not knowingly collect personal information from minors. If you become aware that a child has provided us with personal information, please contact us.

## 11. Third-Party Links

Our service may contain links to third-party websites. We are not responsible for the privacy practices of these external sites. We encourage you to review their privacy policies.

## 12. Changes to This Privacy Policy

We may update our Privacy Policy from time to time. We will notify you of any changes by:

* Posting the new Privacy Policy on this page
* Updating the "Last Updated" date
* Sending you an email notification (if applicable)

## 13. Contact Information

If you have any questions about this Privacy Policy, please contact us:

**Email:** \[<hi@rozo.ai>]

*This Privacy Policy is effective as of \[2025-08-28] and was last updated on \[2025-08-28].*


# FAQs

**Latest Updated**: \[2025-12-09]

Find out all the essential details about our platform and how it can serve your needs.

## 1. What is ROZO Intents?

ROZO Intents is an intent-based stablecoin SDK that enables users to transfer USDC and other stablecoins between chains like Stellar, Base, Polygon, and Solana with zero confusion and near-zero error. It is designed for real-world payment speed and reliability, combining stablecoin abstraction, automatic route selection for the fastest route, and optimized capital flow with transparent status tracking. ROZO Intents approaches cross-chain transfers as both a technical and user-experience problem — making crypto payments feel as simple as Visa. Rozo is supported by Stellar Community Fund, Base, Draper and Circle Alliance.

## 2. How long does it take to receive my funds?

Most ROZO Intents transfers complete in under few seconds.

* **Fast route available on destination chain:** 5 seconds
* **Requires liquidity rebalance from another chain:** 10–20 minutes
* **Network-level fallback route:** up to 1–2 hours

If your transfer takes longer than the quoted time, you can contact our support team via Discord.

## 3. Which blockchains does ROZO Intents support?

ROZO Intents currently supports transfers between:

* Stellar
* Base
* Polygon
* Solana
* BNB Chain
* Arbitrum & Polygon (coming soon)

## 4. What tokens can I transfer?

ROZO Intents supports stablecoins and assets including:

* USDC
* USDT
* Other stablecoins (coming soon)

## 5. What if my transfer is stuck or delayed?

If a transfer appears delayed beyond the estimated window:

1. Check the status page in your transaction history
2. Verify the on-chain hash on the source and destination networks
3. If still unresolved after 2 hours, contact <hi@rozo.ai> or our Discord Dev Support channel


