Overview

This page documents the v1 endpoints you’ll call from your client. You’ll be provisioned an API key out-of-band by Uptop — drop it into API settings (top-right) and you can exercise every endpoint from this page via the Try it panels.

Administrative actions (creating rewards, configuring sponsors, opening auctions, manual point adjustments, browsing all redemptions) live on the Uptop dashboard, not in your client integration. The Dashboard tab in this app simulates that surface so you can see what your administrators will be doing on the other side.

This environment
https://insomniac-api-dev.uptop.xyz

Try it on this page hits this host.

Production
https://insomniac-api.uptop.xyz

Use this host when you ship your integration.

Authentication

Every request must include your provisioned API key in the Api-Key header. Keys are issued by Uptop — treat them like any other production secret and keep them on the server side. The server also accepts Authorization: Bearer <key> as a convenience.

every request
Api-Key: <your-uptop-api-key>
Content-Type: application/json
typescript / fetch
const response = await fetch(
  "https://insomniac-api-dev.uptop.xyz/v1/auctions",
  {
    method: "GET",
    headers: {
      "Api-Key": process.env.UPTOP_API_KEY!,
    },
  },
);

Errors

Every non-2xx response is wrapped in a stable envelope so you can branch on error.code without parsing strings.

Envelope

FieldTypeDescription
error.codeV1ErrorCodeStable machine-readable code.
error.messagestring
error.statusnumber

Example

error response
{
  "error": {
    "code": "INSUFFICIENT_POINTS",
    "message": "User does not have enough points for this redemption.",
    "status": 400
  }
}

Error codes

CodeHTTPWhen
UNAUTHORIZED401Missing, malformed, or revoked `Api-Key` header.
NOT_FOUND404Unknown `userId`, `auctionId`, `redemptionId`, or `sponsorId`.
VALIDATION_ERROR400DTO validation failed, `rewardId`/`auctionId` mismatch on redemption, or missing `variantSku` for a merch redemption.
AUCTION_CLOSED400Auction is paused, cancelled, or past its end date.
INSUFFICIENT_POINTS400User balance is below the auction's `currentPrice`.
INTERNAL_ERROR500Anything unexpected — check server logs.

Typical client flow

A common end-to-end loop for a client app:

  1. On sign-in, call POST /users:upsert with your externalUserId + email and cache the returned id.
  2. Render the storefront by fetching GET /auctions?status=open and the corresponding GET /rewards/{rewardId}. Display currentPrice as the live points cost.
  3. Show the user’s balance with GET /users/{userId}/points/balance and (optionally) their history via GET /users/{userId}/points/events.
  4. Claim a reward by calling POST /users/{userId}/redemptions. Catch AUCTION_CLOSED and INSUFFICIENT_POINTS explicitly — they’re the two recoverable error states.
  5. Show past redemptions via GET /users/{userId}/redemptions.

Users

2 endpoints

Upsert a user keyed by `email` (idempotent — repeat on every sign-in to keep the record in sync), then retrieve the full profile including live wallet balance by `id`.

Upsert user

Look up an Uptop user by `email` and create it if it doesn't exist. A wallet is provisioned automatically on creation. Cache the returned `id` — it's the only key the rest of the API takes.

POST/v1/users:upsert

Request body

FieldTypeRequiredDescription
emailstring (email)requiredPrimary identifier. Upsert is idempotent on this. Also required for virtual card issuance.
phoneNumberstring (E.164)User's phone number in E.164 format, e.g. `+12025551234`. **Required for virtual card issuance.** Kept in sync — re-upsert with a new value to update it.
externalUserIdstringYour partner-side identifier for this user. Optional. When supplied it's linked to the matched/created record so subsequent partner lookups by it work too.
firstNamestringUser's first name. **Required for virtual card issuance** — used as the cardholder billing name on the virtual card. Kept in sync on every upsert.
lastNamestringUser's last name. **Required for virtual card issuance** — used as the cardholder billing name on the virtual card. Kept in sync on every upsert.
example body
{
  "email": "fan@example.com",
  "phoneNumber": "+12025551234",
  "externalUserId": "partner-user-001",
  "firstName": "Taylor",
  "lastName": "Fan"
}

Response — 200

ReturnsUser

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab34cd",
  "email": "fan@example.com",
  "phoneNumber": "+12025551234",
  "externalUserId": "partner-user-001",
  "firstName": "Taylor",
  "lastName": "Fan",
  "createdAt": "2026-05-22T15:00:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • VALIDATION_ERROR400`email` missing or malformed, or `phoneNumber` supplied but not valid E.164.

Notes

  • **All four fields are required to issue a virtual card: `email`, `phoneNumber`, `firstName`, and `lastName`.** These are used as the cardholder identity on the card record. A user missing any of them cannot be issued a virtual card — pass all four on first upsert.
  • **No formal KYC is performed.** These fields are used for card issuance purposes only. Uptop does not run identity verification or background checks on this information. Wallets are capped at $1,000, which falls below the threshold that would require KYC.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Get user

Fetch the user's profile from Uptop plus their live wallet balance. Use this to display the current balance before prompting a load or redemption.

GET/v1/users/{userId}

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Response — 200

ReturnsUserDetail

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab34cd",
  "email": "fan@example.com",
  "phoneNumber": "+12025551234",
  "externalUserId": "partner-user-001",
  "createdAt": "2026-05-22T15:00:00.000Z",
  "balance": {
    "amount": 2500,
    "currency": "usd"
  }
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `userId`.
  • INTERNAL_ERROR502Wallet service unavailable.

Notes

  • `balance.amount` is in the **smallest currency unit** — cents for USD. Divide by 100 to display dollars.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Wallet

4 endpoints

Virtual wallet attached to every user. Use the virtual-card endpoint to retrieve display-safe card details for showing the card in-app. Transactions are cursor-paginated — store the `nextCursor` from each response and pass it as `cursor` on the next request to page forward through history.

Get virtual card

Returns display-safe virtual card details — network, last four digits, expiry, cardholder name, and state. Does not expose the full card number or CVV.

GET/v1/users/{userId}/wallet/virtual-card

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Response — 200

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "47d688b2-c0d6-46a2-81e5-2baa979a5aa2",
  "type": "VIRTUAL",
  "cardNetwork": "mastercard",
  "lastFour": "8414",
  "expMonth": "08",
  "expYear": "2029",
  "cardHolderName": "Tony Lee",
  "state": "OPEN"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404userId does not exist, no virtual card has been provisioned for this user, or the merchant has no active virtual card program.
  • UNPROCESSABLE_ENTITY422The user is missing one or more fields required for virtual-card operations: `email`, `phoneNumber`, `firstName`, or `lastName`. Upsert the user with all four fields first.
  • INTERNAL_ERROR502Wallet service unavailable.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

List wallet transactions

Returns a cursor-paginated list of wallet transactions for a user, newest first. Omit `cursor` for the first page; pass the returned `nextCursor` on each subsequent call.

GET/v1/users/{userId}/wallet/transactions

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Query parameters

FieldTypeRequiredDescription
limitintegerMax transactions per page. Default `20`, max `50`.
cursorstringPagination cursor from a previous response's `nextCursor`. Omit for the first page.

Response — 200

FieldTypeDescription
transactionsWalletTransaction[]Ordered newest-first.
hasMoreboolean`true` when additional pages exist.
nextCursorstringPass as `cursor` to fetch the next page. Absent when `hasMore` is `false`.
example response (200)
{
  "transactions": [
    {
      "id": "txn-abc123",
      "type": "use_balance",
      "amount": -1500,
      "currency": "usd",
      "createdAt": "2026-06-01T18:30:00Z",
      "description": "Spend"
    },
    {
      "id": "txn-def456",
      "type": "add_balance",
      "amount": 10000,
      "currency": "usd",
      "createdAt": "2026-05-28T12:00:00Z",
      "description": "Card load"
    }
  ],
  "hasMore": true,
  "nextCursor": "cursor_abc123"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404userId does not exist.
  • INTERNAL_ERROR502Wallet service unavailable.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

List payment methods

Returns the funding sources attached to the user's wallet — the cards (and bank accounts) used to load balance. Each entry is display-safe: brand and last four only, never the full number. The `preferred` method is the default used by add-balance when no `paymentMethodId` is supplied. Returns an empty array when no payment method is on file.

GET/v1/users/{userId}/wallet/payment-methods

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Response — 200

FieldTypeDescription
paymentMethodsPaymentMethod[]Attached funding sources. Empty when none are on file.
example response (200)
{
  "paymentMethods": [
    {
      "id": "pm-9f2c1a7b",
      "type": "card",
      "brand": "visa",
      "last4": "4242",
      "expMonth": 12,
      "expYear": 2027,
      "preferred": true
    }
  ]
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404userId does not exist.
  • INTERNAL_ERROR502Wallet service unavailable.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Create & push-provision virtual card

Issues the user's virtual card (if they don't have one yet) and pushes it into Apple Pay or Google Pay in a single call. The virtual card is a singleton per user, so this endpoint is idempotent — it reuses the existing card when one is present and only creates a card the first time. The device generates cryptographic material (nonce, nonce signature, certificate) which this endpoint forwards to the card network. An opaque provisioning payload is returned for the device to complete the "Add to Wallet" flow.

POST/v1/users/{userId}/wallet/virtual-card/provision

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Request body

FieldTypeRequiredDescription
digitalWallet"APPLE_PAY" | "GOOGLE_PAY"requiredTarget digital wallet.
noncestringApple Pay only. Base64-encoded cryptographic nonce generated by the device.
nonceSignaturestringApple Pay only. Base64-encoded signature over the nonce generated by the device.
certificatestringApple Pay only. Apple's public leaf certificate, Base64-encoded PEM (headers omitted).
example body
{
  "digitalWallet": "APPLE_PAY",
  "nonce": "base64nonce==",
  "nonceSignature": "base64sig==",
  "certificate": "base64cert=="
}

Response — 200

FieldTypeDescription
(provisioning payload)objectOpaque cryptographic data. Pass this directly to the device's wallet SDK to complete provisioning.
example response (200)
{}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404userId does not exist, or the merchant has no active virtual card program (no card can be issued).
  • VALIDATION_ERROR400digitalWallet is missing, or nonce/nonceSignature/certificate are absent for APPLE_PAY.
  • UNPROCESSABLE_ENTITY422The user is missing one or more fields required for virtual-card operations: `email`, `phoneNumber`, `firstName`, or `lastName`. Upsert the user with all four fields first.
  • INTERNAL_ERROR502Card provisioning service unavailable.

Notes

  • This is the only endpoint that issues a virtual card — it creates one on the first call and reuses it on every call after, then push-provisions it. Because the card is a singleton, the endpoint is idempotent and safe to retry.
  • **Cardholder-identity prerequisites — all four must be on file:** `email`, `phoneNumber`, `firstName`, `lastName`. They become the cardholder identity on the card record and are required by every virtual-card endpoint (issue, push-provision, and fetch). Upsert the user with all four before provisioning, or you'll get a 422.
  • **No formal KYC is performed.** These fields are used for card issuance only — Uptop does not run identity verification on them. The $1,000 wallet cap keeps the program below the threshold that would require KYC.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Sponsors

2 endpoints

Sponsors are the brands whose spend triggers points (linked-card, receipt scan, or both). Configuration happens on the Uptop dashboard — you only need to read them to render sponsor cards in-app.

List sponsors

All sponsors, in registration order.

GET/v1/sponsors

Response — 200

ReturnsSponsor[]

Click the type for the full field-by-field shape.

example response (200)
[
  {
    "id": "65f3a91d6f3a4b0012ab40aa",
    "name": "Acme Coffee",
    "description": "Local coffee partner",
    "logoUrl": "https://example.com/acme.png",
    "primaryColor": "#1D428A",
    "secondaryColor": "#FFFFFF",
    "matchingType": "receipt",
    "pointsMultiplier": 2,
    "frontendMultiplier": 2,
    "priority": 10,
    "unavailable": false,
    "createdAt": "2026-05-22T15:00:00.000Z"
  }
]

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Get sponsor

Full record for a single sponsor.

GET/v1/sponsors/{sponsorId}

Path parameters

FieldTypeRequiredDescription
sponsorIdstringrequiredSponsor id from the list endpoint.

Response — 200

ReturnsSponsor

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab40aa",
  "name": "Acme Coffee",
  "description": "Local coffee partner",
  "logoUrl": "https://example.com/acme.png",
  "primaryColor": "#1D428A",
  "secondaryColor": "#FFFFFF",
  "matchingType": "receipt",
  "pointsMultiplier": 2,
  "frontendMultiplier": 2,
  "priority": 10,
  "unavailable": false,
  "createdAt": "2026-05-22T15:00:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `sponsorId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Rewards

2 endpoints

Rewards are the things fans can redeem (tickets, experiences, merch, collectibles). They carry no `pointsCost` — price comes from the auction. Created/edited from the Uptop dashboard; clients only need to list/read.

List rewards

All rewards.

GET/v1/rewards

Response — 200

ReturnsReward[]

Click the type for the full field-by-field shape.

example response (200)
[
  {
    "id": "65f3a91d6f3a4b0012ab50bb",
    "name": "VIP locker room tour",
    "description": "Walk-through after the game.",
    "iconUrl": "https://example.com/icon.png",
    "buttonColor": "#1D428A",
    "type": "experience",
    "formFields": [
      {
        "name": "first_name",
        "type": "text",
        "required": true
      },
      {
        "name": "last_name",
        "type": "text",
        "required": true
      },
      {
        "name": "phone_number",
        "type": "text",
        "required": true
      }
    ],
    "tags": [
      "members-only"
    ],
    "createdAt": "2026-05-22T15:00:00.000Z"
  }
]

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Get reward

Full record for a single reward, including its `formFields` (what the user has to fill in to redeem) and merch variants if applicable.

GET/v1/rewards/{rewardId}

Path parameters

FieldTypeRequiredDescription
rewardIdstringrequiredReward id from the list endpoint.

Response — 200

ReturnsReward

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab50bb",
  "name": "Home jersey",
  "description": "Replica home jersey",
  "iconUrl": "https://example.com/j.png",
  "buttonColor": "#FFFFFF",
  "type": "merch",
  "formFields": [
    {
      "name": "first_name",
      "type": "text",
      "required": true
    },
    {
      "name": "last_name",
      "type": "text",
      "required": true
    },
    {
      "name": "address_line_1",
      "type": "text",
      "required": true
    },
    {
      "name": "city",
      "type": "text",
      "required": true
    },
    {
      "name": "state",
      "type": "text",
      "required": true
    },
    {
      "name": "zip",
      "type": "text",
      "required": true
    }
  ],
  "merch": {
    "variants": [
      {
        "sku": "home-jersey-M",
        "color": "blue",
        "size": "M",
        "inventory": 5
      }
    ]
  },
  "tags": [
    "fan-favorite",
    "summer-drop"
  ],
  "createdAt": "2026-05-22T15:00:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • VALIDATION_ERROR400Unknown `rewardId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Auctions

2 endpoints

Auctions price rewards in points. `currentPrice` is calculated on read for `open`/`paused` auctions and is `null` for `ended`/`cancelled`. Create/edit/pause/resume/cancel are dashboard-driven; you'll only ever read them.

List auctions

All auctions; optionally filter by status. Pass `expand=reward` to inline each auction's full `Reward` and render a storefront in one request.

GET/v1/auctions

Query parameters

FieldTypeRequiredDescription
status"open" | "paused" | "cancelled" | "ended"Filter by lifecycle state.
expand"reward"When set, every row carries the full `Reward` object under `reward` — same shape as `GET /rewards/{rewardId}` — instead of just `rewardId`. Saves the per-auction reward fetch (1 + N calls become 1).

Response — 200

ReturnsAuction[]

Click the type for the full field-by-field shape.

example response (200)
[
  {
    "id": "65f3a91d6f3a4b0012ab60cc",
    "rewardId": "65f3a91d6f3a4b0012ab50bb",
    "startPrice": 1000,
    "reservePrice": 100,
    "currentPrice": 750,
    "startDate": "2026-05-22T14:00:00.000Z",
    "endDate": "2026-05-22T16:00:00.000Z",
    "quantity": 5,
    "quantityPurchased": 1,
    "status": "open",
    "createdAt": "2026-05-22T13:50:00.000Z"
  }
]

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.

Notes

  • The example above shows the default (unexpanded) response. With `?expand=reward`, each row additionally includes a `reward` object — see the `Auction` type for the field and the `Reward` type for its shape.
  • If an auction's underlying reward no longer exists, the `reward` field is omitted for that row rather than returning an error.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Get auction

Single auction. `currentPrice` is computed at read time. Supports `expand=reward` like the list endpoint.

GET/v1/auctions/{auctionId}

Path parameters

FieldTypeRequiredDescription
auctionIdstringrequiredAuction id.

Query parameters

FieldTypeRequiredDescription
expand"reward"When set, the full `Reward` object is inlined under `reward`.

Response — 200

ReturnsAuction

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab60cc",
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "startPrice": 1000,
  "reservePrice": 100,
  "currentPrice": 750,
  "startDate": "2026-05-22T14:00:00.000Z",
  "endDate": "2026-05-22T16:00:00.000Z",
  "quantity": 5,
  "quantityPurchased": 1,
  "status": "open",
  "createdAt": "2026-05-22T13:50:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `auctionId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Points

2 endpoints

Per-user points balance and ledger. Manual adjustments are admin-only and handled from the dashboard.

Get user points balance

Available, pending, and lifetime-earned points. Card-spend earns start in `pendingBalance` (estimated from the authorized amount) and move to `currentBalance` (spendable) once the card transaction settles — credited at the amount that actually settled, so the final value can differ from the pending estimate (a fully reversed charge earns none).

GET/v1/users/{userId}/points/balance

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Response — 200

Click the type for the full field-by-field shape.

example response (200)
{
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "currentBalance": 4250,
  "pendingBalance": 750,
  "lifetimeEarned": 5000
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `userId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

List user points events

Ledger of point movements for the user, newest first. Card spend surfaces as `'spend'`; other internal types (referrals, badges, …) collapse to `'other'`.

GET/v1/users/{userId}/points/events

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Query parameters

FieldTypeRequiredDescription
type"adjustment" | "receipt" | "daily_claim" | "reward_claim" | "spend"Optional filter. Only the public event types are exposed by name.

Response — 200

Click the type for the full field-by-field shape.

example response (200)
[
  {
    "id": "65f3a91d6f3a4b0012ab70dd",
    "userId": "65f3a91d6f3a4b0012ab34cd",
    "type": "adjustment",
    "delta": 5000,
    "reason": "manual_seed",
    "createdAt": "2026-05-22T15:01:00.000Z"
  },
  {
    "id": "65f3a91d6f3a4b0012ab70ee",
    "userId": "65f3a91d6f3a4b0012ab34cd",
    "type": "reward_claim",
    "delta": -750,
    "createdAt": "2026-05-22T15:10:00.000Z"
  }
]

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `userId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Redemptions

3 endpoints

A redemption claims an open auction at its `currentPrice`, deducting available points FIFO from the user's ledger. Pending (unsettled card-spend) points can't be redeemed. This is the most important client-side write — it's how a fan actually 'buys' a reward.

Create redemption

Claim an auction on behalf of the user. Pass both `rewardId` and `auctionId` — the server rejects mismatches. Merch redemptions additionally require `variantSku`.

POST/v1/users/{userId}/redemptions

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Request body

FieldTypeRequiredDescription
rewardIdstringrequired
auctionIdstringrequired
completedFormFieldsRecord<string, string>requiredValues for the reward's `formFields`. For experience, send `phone_number`; for merch/collectible, send name + address fields.
variantSkustringRequired for merch rewards. Must match a variant on the reward.
example body
{
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "auctionId": "65f3a91d6f3a4b0012ab60cc",
  "completedFormFields": {
    "first_name": "Test",
    "last_name": "Fan",
    "phone_number": "555-555-1212"
  }
}

Response — 201

ReturnsRedemption

Click the type for the full field-by-field shape.

example response (201)
{
  "id": "65f3a91d6f3a4b0012ab80ff",
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "pointsSpent": 750,
  "completedFormFields": {
    "first_name": "Test",
    "last_name": "Fan",
    "phone_number": "555-555-1212"
  },
  "createdAt": "2026-05-22T15:10:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `userId` or `auctionId`.
  • VALIDATION_ERROR400`rewardId` doesn't match the auction's reward, or `variantSku` missing on a merch redemption.
  • AUCTION_CLOSED400Auction is paused, cancelled, or past its end date.
  • INSUFFICIENT_POINTS400User balance is below `currentPrice`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

List user redemptions

Every redemption belonging to the user, newest first.

GET/v1/users/{userId}/redemptions

Path parameters

FieldTypeRequiredDescription
userIdstringrequiredUptop user id returned from `POST /users:upsert`.

Response — 200

Click the type for the full field-by-field shape.

example response (200)
[
  {
    "id": "65f3a91d6f3a4b0012ab80ff",
    "rewardId": "65f3a91d6f3a4b0012ab50bb",
    "userId": "65f3a91d6f3a4b0012ab34cd",
    "pointsSpent": 750,
    "completedFormFields": {
      "first_name": "Test",
      "last_name": "Fan",
      "phone_number": "555-555-1212"
    },
    "createdAt": "2026-05-22T15:10:00.000Z"
  }
]

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `userId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Get redemption

A single redemption by id.

GET/v1/redemptions/{redemptionId}

Path parameters

FieldTypeRequiredDescription
redemptionIdstringrequiredRedemption id.

Response — 200

ReturnsRedemption

Click the type for the full field-by-field shape.

example response (200)
{
  "id": "65f3a91d6f3a4b0012ab80ff",
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "pointsSpent": 750,
  "completedFormFields": {
    "first_name": "Test",
    "last_name": "Fan",
    "phone_number": "555-555-1212"
  },
  "createdAt": "2026-05-22T15:10:00.000Z"
}

Errors

  • UNAUTHORIZED401Missing or invalid `Api-Key`.
  • NOT_FOUND404Unknown `redemptionId`.

Try it (curl)

curl
Set an API key in the top-right API settings menu —$UPTOP_API_KEY in the curl will be substituted for it on send.

Edit anything above, then send through the local proxy.

Type definitions

Every response above references one of these types. Each definition lists the full field-by-field shape — click any type name elsewhere on the page to jump back here.

An Uptop user. The `id` is what every other endpoint takes as `{userId}`. Returned by `POST /users:upsert`.

FieldTypeRequiredDescription
idstringrequiredUptop user id.
emailstring (email)requiredPrimary identifier. Upsert is idempotent on this.
phoneNumberstring (E.164)Present only if supplied on a previous upsert. **Required for card issuance** — pass it on first upsert or the user cannot receive a virtual card.
externalUserIdstringPartner-side identifier. Present only if supplied on a previous upsert.
firstNamestringPresent only if supplied on a previous upsert. **Required for virtual card issuance.**
lastNamestringPresent only if supplied on a previous upsert. **Required for virtual card issuance.**
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab34cd",
  "email": "fan@example.com",
  "phoneNumber": "+12025551234",
  "externalUserId": "partner-user-001",
  "firstName": "Taylor",
  "lastName": "Fan",
  "createdAt": "2026-05-22T15:00:00.000Z"
}

WalletTransaction

#type-WalletTransaction

A single wallet credit or debit. `use_balance` transactions are debits and carry a negative `amount`.

FieldTypeRequiredDescription
idstringrequiredTransaction ID.
typestringrequiredTransaction type. Common values: `add_balance`, `use_balance`, `merchant_refund`.
amountintegerrequiredCents. Negative for `use_balance` (debit); positive for credits.
currencystringrequiredLowercase ISO 4217, e.g. `"usd"`.
createdAtstring (ISO 8601)requiredUTC timestamp.
descriptionstringHuman-readable label. Omitted if not set.
example
{
  "id": "txn-abc123",
  "type": "use_balance",
  "amount": -1500,
  "currency": "usd",
  "createdAt": "2026-06-01T18:30:00Z",
  "description": "Spend"
}

Extends `User` with a live wallet balance. Returned by `GET /users/{userId}`.

FieldTypeRequiredDescription
idstringrequired
emailstring (email)required
phoneNumberstring (E.164)Present only if supplied on a previous upsert. **Required for card issuance.**
externalUserIdstringPresent only if supplied on a previous upsert.
firstNamestringPresent only if supplied on a previous upsert. **Required for virtual card issuance.**
lastNamestringPresent only if supplied on a previous upsert. **Required for virtual card issuance.**
createdAtstring (ISO 8601)required
balance.amountnumberrequiredWallet balance in the **smallest currency unit** (cents for USD). Divide by 100 to display dollars.
balance.currencystringrequiredLowercase ISO 4217 currency code, e.g. `"usd"`.
example
{
  "id": "65f3a91d6f3a4b0012ab34cd",
  "email": "fan@example.com",
  "phoneNumber": "+12025551234",
  "externalUserId": "partner-user-001",
  "firstName": "Taylor",
  "lastName": "Fan",
  "createdAt": "2026-05-22T15:00:00.000Z",
  "balance": {
    "amount": 2500,
    "currency": "usd"
  }
}

Display-safe virtual card details. Does not include the full card number or CVV.

FieldTypeRequiredDescription
idstringrequiredVirtual card UUID.
typestringrequiredAlways `"VIRTUAL"`.
cardNetworkstringrequiredCard network, e.g. `"mastercard"`.
lastFourstringrequiredLast four digits of the card number.
expMonthstringrequiredZero-padded expiry month, e.g. `"08"`.
expYearstringrequiredFour-digit expiry year, e.g. `"2029"`.
cardHolderNamestringrequiredCardholder name on the virtual card.
statestringrequiredCard state, e.g. `"OPEN"`.
example
{
  "id": "47d688b2-c0d6-46a2-81e5-2baa979a5aa2",
  "type": "VIRTUAL",
  "cardNetwork": "mastercard",
  "lastFour": "8414",
  "expMonth": "08",
  "expYear": "2029",
  "cardHolderName": "Tony Lee",
  "state": "OPEN"
}

PaymentMethod

#type-PaymentMethod

A display-safe funding source attached to a wallet (a card or bank account used to load balance). Never includes the full account number. Returned by `GET /users/{userId}/wallet/payment-methods`.

FieldTypeRequiredDescription
idstringrequiredPayment method ID. Pass as `paymentMethodId` to add-balance to charge a specific source.
typestringFunding source type, e.g. `"card"` or `"bank_account"`. Omitted when unknown.
brandstringFor cards, the network (e.g. `"visa"`); for bank accounts, the bank name. Omitted when unknown.
last4stringLast four digits of the card or account number. Omitted when unknown.
expMonthintegerCard expiry month (`1`–`12`). Cards only; omitted for bank accounts.
expYearintegerFour-digit card expiry year. Cards only; omitted for bank accounts.
preferredbooleanrequired`true` for the default source used by add-balance when no `paymentMethodId` is given.
example
{
  "id": "pm-9f2c1a7b",
  "type": "card",
  "brand": "visa",
  "last4": "4242",
  "expMonth": 12,
  "expYear": 2027,
  "preferred": true
}

A brand whose spend triggers points. Configuration is dashboard-driven.

FieldTypeRequiredDescription
idstringrequired
namestringrequired
descriptionstringrequired
logoUrlstring (url)required
primaryColorstring (hex)required
secondaryColorstring (hex)required
matchingType"plaid" | "receipt" | "combination"requiredHow spend is matched. `combination` accepts both linked-card and receipt evidence.
pointsMultipliernumberrequiredServer-side reward multiplier on matched spend.
frontendMultipliernumberrequiredDisplay-only multiplier rendered in the app.
prioritynumberrequiredHigher values surface earlier in the sponsor strip.
unavailablebooleanrequiredWhen true, hide the sponsor without deleting it.
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab40aa",
  "name": "Acme Coffee",
  "description": "Local coffee partner",
  "logoUrl": "https://example.com/acme.png",
  "primaryColor": "#1D428A",
  "secondaryColor": "#FFFFFF",
  "matchingType": "receipt",
  "pointsMultiplier": 2,
  "frontendMultiplier": 2,
  "priority": 10,
  "unavailable": false,
  "createdAt": "2026-05-22T15:00:00.000Z"
}

A redeemable item (ticket, experience, merch, collectible). Rewards carry no `pointsCost` — price comes from the auction. `merch` is only present when `type === 'merch'`.

FieldTypeRequiredDescription
idstringrequired
namestringrequired
descriptionstringrequired
iconUrlstring (url)required
buttonColorstring (hex)required
type"ticket" | "experience" | "merch" | "collectible"required
formFieldsFormField[]requiredFields the user must fill in to redeem this reward.
merch{ variants: MerchVariant[] }Present only when `type === 'merch'`. Pass the chosen `variantSku` on redemption.
tagsstring[]requiredFree-form editorial labels (e.g. `summer-drop`, `edc-exclusive`) for grouping/featuring beyond `type`. Always an array — `[]` when none are set.
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab50bb",
  "name": "Home jersey",
  "description": "Replica home jersey",
  "iconUrl": "https://example.com/j.png",
  "buttonColor": "#FFFFFF",
  "type": "merch",
  "formFields": [
    {
      "name": "first_name",
      "type": "text",
      "required": true
    },
    {
      "name": "last_name",
      "type": "text",
      "required": true
    }
  ],
  "merch": {
    "variants": [
      {
        "sku": "home-jersey-M",
        "color": "blue",
        "size": "M",
        "inventory": 5
      }
    ]
  },
  "tags": [
    "fan-favorite",
    "summer-drop"
  ],
  "createdAt": "2026-05-22T15:00:00.000Z"
}

A single input the user has to provide to claim a reward.

FieldTypeRequiredDescription
namestringrequiredField key (e.g. `first_name`, `phone_number`).
type"text" | "select" | "email"required
requiredbooleanrequired
example
{
  "name": "phone_number",
  "type": "text",
  "required": true
}

MerchVariant

#type-MerchVariant

A purchasable SKU on a merch `Reward`.

FieldTypeRequiredDescription
skustringrequiredVariant SKU. Pass this as `variantSku` on a merch redemption.
colorstringrequired
sizestringrequired
inventorynumberrequiredRemaining stock.
example
{
  "sku": "home-jersey-M",
  "color": "blue",
  "size": "M",
  "inventory": 5
}

A Dutch-style auction pricing a `Reward` in points. `currentPrice` is `null` when the auction is `ended` or `cancelled`.

FieldTypeRequiredDescription
idstringrequired
rewardIdstringrequiredReward being auctioned.
rewardRewardFull reward object. Present only when the request used `?expand=reward`; omitted if the underlying reward no longer exists.
startPricenumberrequiredPoints cost at `startDate`.
reservePricenumberrequiredFloor the auction decays toward.
currentPricenumber | nullrequiredComputed at read time for `open`/`paused` auctions. `null` for `ended`/`cancelled`.
startDatestring (ISO 8601)required
endDatestring (ISO 8601)required
quantitynumberrequiredTotal claimable units.
quantityPurchasednumberrequired
status"open" | "paused" | "cancelled" | "ended"required
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab60cc",
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "startPrice": 1000,
  "reservePrice": 100,
  "currentPrice": 750,
  "startDate": "2026-05-22T14:00:00.000Z",
  "endDate": "2026-05-22T16:00:00.000Z",
  "quantity": 5,
  "quantityPurchased": 1,
  "status": "open",
  "createdAt": "2026-05-22T13:50:00.000Z"
}

PointsBalance

#type-PointsBalance

Current and lifetime points totals for a user.

FieldTypeRequiredDescription
userIdstringrequired
currentBalancenumberrequiredSpendable (available) points right now.
pendingBalancenumberrequiredPoints from card taps awaiting settlement — not yet redeemable. Estimated from the authorized amount; the amount credited to `currentBalance` reflects what actually settles, so it can differ (a fully reversed charge earns none).
lifetimeEarnednumberrequiredTotal points ever credited (never decreases).
example
{
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "currentBalance": 4250,
  "pendingBalance": 750,
  "lifetimeEarned": 5000
}

An immutable ledger entry. The four public `type` values are exposed by name; all internal event types (referrals, badges, wallet spend, …) collapse to `'other'`.

FieldTypeRequiredDescription
idstringrequired
userIdstringrequired
type"adjustment" | "receipt" | "daily_claim" | "reward_claim" | "spend" | "other"required`spend` is a card-spend earn.
deltanumberrequiredPositive credits the balance; negative debits it.
reasonstringFree-form. Typically only set on `adjustment` events.
sponsorIdstringThe sponsor whose spend triggered the earn, for theming Tokens. Set on sponsor-attributed card earns; absent otherwise.
citystringMerchant city where a card-spend (`spend`) earn occurred. Set when the card event carries location; absent otherwise.
statestringMerchant state/region for a card-spend (`spend`) earn. Set when the card event carries location; absent otherwise.
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab70dd",
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "type": "adjustment",
  "delta": 5000,
  "reason": "manual_seed",
  "createdAt": "2026-05-22T15:01:00.000Z"
}

A claimed auction. `pointsSpent` is the auction's `currentPrice` at the moment of claim — not the reward's nominal price.

FieldTypeRequiredDescription
idstringrequired
rewardIdstringrequired
userIdstringrequired
pointsSpentnumberrequiredAuction `currentPrice` at claim time.
completedFormFieldsRecord<string, string>requiredValues the user submitted for the reward's `formFields`.
createdAtstring (ISO 8601)required
example
{
  "id": "65f3a91d6f3a4b0012ab80ff",
  "rewardId": "65f3a91d6f3a4b0012ab50bb",
  "userId": "65f3a91d6f3a4b0012ab34cd",
  "pointsSpent": 750,
  "completedFormFields": {
    "first_name": "Test",
    "last_name": "Fan",
    "phone_number": "555-555-1212"
  },
  "createdAt": "2026-05-22T15:10:00.000Z"
}