> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.upwardli.com/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 <token>
```

### 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 <token>" \
  -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 <token>"
```

### 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 <token>"
```

### 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                  |