> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://developers.upwardli.com/guides/p-2-p-payments/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.upwardli.com/_mcp/server. # P2P Payments > Transfer funds between two consumers on your partner network using the P2P Payments API. ## Overview P2P (peer-to-peer) payments let one consumer send funds directly to another consumer within your partner network — no external bank accounts or card networks involved. Both the sender and receiver must hold a secured-card account (or secured-card subledger account) managed by your partner. **Common use cases:** * Splitting bills between users * Gifting or rewarding another user on your platform * Intra-network fund transfers initiated by your application Token scope required: `api:write` or `api:purchase:write` --- ## Prerequisites * Valid OAuth 2.0 Bearer token ([see Authentication](/concepts/authentication/introduction)) * Both consumers must have active secured-card accounts on your partner network * The sending consumer must have sufficient available balance * P2P payments must be enabled for your partner configuration --- ## How It Works ``` Partner App Upwardli API CRB (Core Banking) │ │ │ │ POST /v2/payments/p2p/ │ │ │─────────────────────────>│ │ │ │ Validate accounts │ │ │ Check balance │ │ │ Check velocity limit │ │ │ │ │ │ Create ledger entry │ │ │─────────────────────────>│ │ │ │ │ │ Submit to XPay │ │ │─────────────────────────>│ │ │ │ │ 201 { id, status } │ │ │<─────────────────────────│ │ │ │ │ │ │ Webhook: Payment.P2P.Created │<─────────────────────────│ │ │ │ │ │ │ Webhook: Payment.P2P.Completed (async) │<─────────────────────────│ │ ``` ### Payment statuses | Status | Meaning | | ---------------- | ----------------------------------------------------------- | | `pending` | Payment submitted and in process | | `posted` | Funds settled — transfer complete | | `pending_review` | Payment held for compliance review (velocity limit reached) | | `error` | Submission failed; contact support if it persists | | `canceled` | Payment was canceled | | `returned` | Payment was returned by the receiving side | ### Compliance velocity hold Upwardli enforces a daily velocity limit of **2 P2P transfers per consumer per 24-hour window** (counting both sends and receives). When a consumer exceeds this limit, the payment is created with status `pending_review` instead of being rejected. Your application receives the normal 201 response with `status: "pending_review"`. A `Payment.P2P.Created` webhook is **not** sent for held payments. Once an Upwardli compliance agent reviews and approves the payment, it transitions to `pending` and proceeds normally, triggering the webhook at that point. --- ## API Endpoints ### Base URLs ``` Production: https://api.upwardli.com Sandbox: https://api-sandbox.upwardli.com ``` --- ## 1. Create a P2P Payment ### Endpoint ``` POST /v2/payments/p2p/ ``` ### Authentication ``` Authorization: Bearer ``` ### Request body | Field | Type | Required | Description | | ------------------------ | ------ | -------- | ---------------------------------------------------------------------------------- | | `originating_account_id` | UUID | Yes | External ID of the sending consumer's account | | `receiving_account_id` | UUID | Yes | External ID of the receiving consumer's account | | `amount` | float | Yes | Transfer amount in USD. Minimum `1.00`. Maximum set by your partner configuration. | | `description` | string | No | Optional memo visible to both parties | > **Note** > > The `memo` field is not accepted on P2P payments and will cause a validation error if included. ### Example request ```bash curl -X POST https://api.upwardli.com/v2/payments/p2p/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "originating_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "receiving_account_id": "7cb94a21-1a3b-4891-9e12-6d1f5a2bc099", "amount": 25.00, "description": "Splitting dinner" }' ``` ### Response — 201 Created ```json { "id": "a1b2c3d4-0000-4000-8000-000000000001", "originating_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "receiving_account_id": "7cb94a21-1a3b-4891-9e12-6d1f5a2bc099", "amount": 25.00, "status": "pending", "description": "Splitting dinner" } ``` **Compliance-held response (same 201, different status):** ```json { "id": "a1b2c3d4-0000-4000-8000-000000000002", "originating_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "receiving_account_id": "7cb94a21-1a3b-4891-9e12-6d1f5a2bc099", "amount": 25.00, "status": "pending_review", "description": "Splitting dinner" } ``` ### Full API reference [Create P2P Payment →](/api-access/api-reference/payments/p-2-p/create-p-2-p-payment) --- ## 2. Retrieve a P2P Payment ### Endpoint ``` GET /v2/payments/p2p/{id}/ ``` ### Path parameters | Parameter | Type | Description | | --------- | ---- | ---------------------------------------------- | | `id` | UUID | The `id` returned when the payment was created | ### Example request ```bash curl https://api.upwardli.com/v2/payments/p2p/a1b2c3d4-0000-4000-8000-000000000001/ \ -H "Authorization: Bearer " ``` ### Response — 200 OK ```json { "id": "a1b2c3d4-0000-4000-8000-000000000001", "originating_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "receiving_account_id": "7cb94a21-1a3b-4891-9e12-6d1f5a2bc099", "amount": 25.00, "status": "posted", "description": "Splitting dinner" } ``` > **Note** > > Retrieve returns a 404 for any payment that does not belong to your partner — this is intentional and prevents information leakage. ### Full API reference [Get P2P Payment →](/api-access/api-reference/payments/p-2-p/get-p-2-p-payment) --- ## 3. List P2P Payments Returns a paginated list of all P2P payments across every consumer on your partner network. ### Endpoint ``` GET /v2/payments/p2p/ ``` ### Query parameters | Parameter | Type | Default | Description | | ----------- | ------- | ------- | ---------------- | | `page` | integer | 1 | Page number | | `page_size` | integer | 20 | Results per page | ### Example request ```bash curl "https://api.upwardli.com/v2/payments/p2p/?page=1&page_size=20" \ -H "Authorization: Bearer " ``` ### Response — 200 OK ```json { "count": 42, "next": "https://api.upwardli.com/v2/payments/p2p/?page=2&page_size=20", "previous": null, "results": [ { "id": "a1b2c3d4-0000-4000-8000-000000000001", "originating_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "receiving_account_id": "7cb94a21-1a3b-4891-9e12-6d1f5a2bc099", "amount": 25.00, "status": "posted", "description": "Splitting dinner" } ] } ``` ### Full API reference [List P2P Payments →](/api-access/api-reference/payments/p-2-p/list-p-2-p-payments) --- ## Webhooks Subscribe to P2P payment events to track payment lifecycle in real time. | Event | When it fires | | ----------------------- | --------------------------------------------- | | `Payment.P2P.Created` | Payment successfully submitted and in process | | `Payment.P2P.Completed` | Funds settled — transfer complete | | `Payment.P2P.Failed` | Submission or settlement failed | | `Payment.P2P.Canceled` | Payment canceled (typically via admin action) | > **Note** > > `Payment.P2P.Created` is **not** sent for payments in `pending_review` status. It fires only once the payment has been approved and submitted to the core banking network. Register your webhook endpoint to receive these events: [Webhook Registration →](/concepts/webhooks/registration) [Full Webhook Event Catalog →](/concepts/webhooks/event-catalog#p2p-payment-webhooks) --- ## Error Reference ### Validation errors (422) These are returned in the response body when the request fails business-rule validation. | `error_code` | Cause | How to resolve | | ----------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------- | | `feature_unavailable` | P2P payments are not enabled for your partner configuration | Contact Upwardli to enable P2P | | `invalid_amount` | Amount is below \$1.00 or exceeds your partner's configured limit | Adjust the amount | | `invalid_account_types` | One or both accounts are not secured-card accounts | Confirm the account external IDs are correct | | `invalid_account_pair` | Originating and receiving accounts are the same | Use two distinct accounts | | `not_found` | One or both accounts do not belong to your partner | Verify the account IDs belong to consumers on your network | | `insufficient_funds` | Sender's available balance is below the requested amount | Ask the sender to fund their account first | | `invalid_account` | No payment card found for the originating account | The account may not be fully provisioned | ### Other errors | HTTP status | Cause | | ----------- | ---------------------------------------------------------------- | | `404` | Payment ID not found or does not belong to your partner | | `401` | Missing or invalid Bearer token | | `403` | Token scope does not include `api:write` or `api:purchase:write` | | `500` | Unexpected server error — open a support ticket | > Transfer funds between two consumers on your partner network using the P2P Payments API.