> ## Documentation Index
> Fetch the complete documentation index at: https://glide-9da73dea.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading user data

> Read consented verification status, wallet addresses, USD receiving details and personal ledger history, and request a hosted verification link.

Four read endpoints and one action. Each needs its own consent scope, and each returns only what the user approved. All of them accept a UUID user id in the path and return HTTP 200 on success with `Cache-Control: no-store` and an `X-Request-Id`.

Every read is limited to 300 requests per minute per partner, except the hosted verification link, which is 10 per minute.

## Verification status

`GET /api/partner/users/:id/kyc` with scope `kyc:read`

```bash theme={null}
curl --request GET \
  "https://glide.co/api/partner/users/11111111-1111-4111-8111-111111111111/kyc" \
  --header "Authorization: Bearer $GLIDE_ACCESS_TOKEN"
```

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "status": "review",
  "provider": "noah",
  "updated_at": "2026-09-06T00:00:00.000Z"
}
```

| Field        | Values                                                         |
| ------------ | -------------------------------------------------------------- |
| `status`     | `not_started`, `in_progress`, `review`, `approved`, `rejected` |
| `provider`   | Verification provider slug, or `null`                          |
| `updated_at` | ISO 8601 timestamp, or `null`                                  |

With no record on file, `status` is `not_started` and both `provider` and `updated_at` are `null`.

You never receive documents, rejection reasons, provider tokens or verification data blobs. This endpoint reads the state that Glide's own verification workflows maintain, so it agrees with what the user sees in Glide.

## Wallets

`GET /api/partner/users/:id/wallets` with scope `wallet:read`

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "wallets": [
    {
      "network": "evm",
      "address": "0x1111111111111111111111111111111111111111",
      "provider": "privy"
    }
  ]
}
```

Networks are `evm` and `solana`. Addresses are the authoritative user columns, refreshed when the user logs in. A user who has not claimed their account normally has an empty array, so treat an empty list as "not ready yet" rather than an error.

You never receive private keys, signing capability, smart accounts or entity vaults.

## USD receiving account

`GET /api/partner/users/:id/us-account` with scope `us_account:read`

Provisioned:

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "status": "provisioned",
  "provider": "noah",
  "currency": "USD",
  "account": {
    "account_number": "1234567890",
    "routing_number": "021000021",
    "bank_name": "Example Bank",
    "account_holder_name": "Example Creator",
    "bank_address": null,
    "swift_code": null,
    "reference": "example-reference"
  },
  "updated_at": "2026-09-06T00:00:00.000Z"
}
```

Not provisioned:

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "status": "not_provisioned"
}
```

The response is a discriminated union on `status`, so branch on it before reading `account`. Account and routing numbers are strings, never numbers. `bank_name`, `account_holder_name`, `bank_address`, `swift_code` and `reference` may be `null`.

This reads only the user's active personal USD receiving account, through an explicit allowlist of wire detail fields. Raw provider payload fields are excluded. A missing account or routing number reports `not_provisioned`.

<Warning>
  This GET never starts provisioning. If a user has no receiving account, the user has to complete the deposit-details flow inside Glide. Polling this endpoint will not create one.
</Warning>

These are the user's personal receiving details from the deposit flow. Personal bank deposits and the partner-funded **Glide balance** are distinct funding paths. They are not funding instructions for your treasury. See [Payouts](/partners/payouts).

## Transfers

`GET /api/partner/users/:id/transfers` with scope `transfers:read`

Returns the user's personal ledger: deposits, withdrawals, transfers and other recorded personal movements. Rows belonging to a business are excluded even when the user id matches.

### Query parameters

| Parameter | Type                             | Notes                                    |
| --------- | -------------------------------- | ---------------------------------------- |
| `since`   | RFC 3339 timestamp with timezone | Exclusive lower bound on `updated_at`    |
| `limit`   | integer, 1 to 100                | Defaults to 50                           |
| `cursor`  | opaque string                    | The `next_cursor` from the previous page |

```bash theme={null}
curl --request GET \
  "https://glide.co/api/partner/users/11111111-1111-4111-8111-111111111111/transfers?since=2026-09-06T00:00:00Z&limit=100" \
  --header "Authorization: Bearer $GLIDE_ACCESS_TOKEN"
```

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "transfers": [
    {
      "id": "22222222-2222-4222-8222-222222222222",
      "type": "transfer",
      "amount_cents": "2500",
      "currency": "USD",
      "status": "completed",
      "created_at": "2026-09-06T00:00:00.000Z",
      "updated_at": "2026-09-06T00:01:00.000000Z",
      "completed_at": "2026-09-06T00:01:00.000Z"
    }
  ],
  "next_cursor": null
}
```

`amount_cents` is an exact string-encoded minor-unit integer, selected as text so no precision is lost in JavaScript. Parse it as a big integer or a decimal type, never as a float. `status`, `currency` and `completed_at` can be `null` on older rows. Raw metadata, beneficiary details and compliance or risk fields are not exposed.

### Paging and polling

Results sort by `updated_at` ascending, then `id` ascending, at full timestamp precision. Preserve your `since` value while paging, follow `next_cursor` until it comes back `null`, and only then advance your watermark.

```ts theme={null}
async function pollTransfers(userId: string, since: string) {
  const rows: unknown[] = [];
  let cursor: string | null = null;

  do {
    const url = new URL(`https://glide.co/api/partner/users/${userId}/transfers`);
    url.searchParams.set('since', since);
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${await getAccessToken()}` },
    });
    if (!res.ok) throw new Error(`glide transfers ${res.status}`);

    const page = (await res.json()) as {
      transfers: { id: string; updated_at: string }[];
      next_cursor: string | null;
    };
    rows.push(...page.transfers);
    cursor = page.next_cursor;
  } while (cursor);

  return rows;
}
```

This is an incremental view, not a frozen snapshot. Rows can change while you page. Overlap your last watermark by a safe margin and deduplicate on `(id, updated_at)` before you apply anything. Doing both makes a repeated row harmless.

An invalid or expired cursor returns `400 invalid_cursor`. Start the page again from your watermark rather than trying to repair the cursor.

## Request a hosted verification link

`POST /api/partner/kyc/:id/link` with scope `kyc:read`, limited to 10 per minute

Send no body.

```bash theme={null}
curl --request POST \
  "https://glide.co/api/partner/kyc/11111111-1111-4111-8111-111111111111/link" \
  --header "Authorization: Bearer $GLIDE_ACCESS_TOKEN"
```

```json theme={null}
{
  "user_id": "11111111-1111-4111-8111-111111111111",
  "status": "created",
  "provider": "noah",
  "url": "https://checkout.noah.com/kyc/example"
}
```

| `status`           | `url`  | Meaning                                            |
| ------------------ | ------ | -------------------------------------------------- |
| `created`          | a URL  | Open it for the user to complete verification      |
| `already_approved` | `null` | The user is already verified. Nothing to do        |
| `pending`          | `null` | The provider is still reviewing and has no new URL |

Glide retains the provider and customer mapping and handles the callbacks, so you get status changes through `kyc.status_changed` rather than by polling the provider. Requests for the same user are serialized, so two concurrent calls will not create two verifications.

The consent copy for `kyc:read` explicitly includes permission to request this link, so no extra consent step is needed.

Failure cases worth handling: a user who already has verification with a different provider returns `409 kyc_provider_conflict`, because partners cannot silently switch providers. Suspended or closed users cannot start verification. Sandbox placeholder URLs are rejected with `503 provider_unavailable`.

## Which user is ready for what

| You want to                                   | The user needs                                                         |
| --------------------------------------------- | ---------------------------------------------------------------------- |
| Receive a payout into their Glide USD balance | An active account, plus `payouts:receive` and `transfers:read` consent |
| Convert USD to USDC on the way to a bank      | Approved verification                                                  |
| Withdraw USDC to a bank                       | A completed conversion, then Glide's bank transfer flow                |

Do not gate your own payout on verification. A user can hold a Glide USD balance long before they finish verifying, and Glide surfaces the verification prompt when they try to cash out.
