---
title: "Create virtual currency transaction"
method: POST
path: "/api/v2/server-side-api/vc/transactions/"
tags: ["Virtual Currency"]
---

# Create virtual currency transaction

`POST /api/v2/server-side-api/vc/transactions/`

Credits or debits one or more virtual currencies for a profile in a single atomic transaction. Use a positive `amount` to credit (grant) and a negative `amount` to debit (spend). All items are applied together — if any item fails, none are applied.

Each currency code may appear only once per request. A credit through this endpoint always creates a non-expiring balance.

## Headers

- `adapty-customer-user-id` string
- `adapty-profile-id` string
- `Idempotency-Key` string, uuid

## Request body

- VirtualCurrencyTransactionRequest — Request body for creating a virtual currency transaction.
  - `items` VirtualCurrencyBalanceAdjustment[], required — Balance adjustments to apply atomically. Each currency code may appear only once.
    - `currency_code` string, required — The virtual currency code to adjust.
    - `amount` integer, required — The amount to apply. A positive value credits (grants) the currency; a negative value debits (spends) it. Cannot be zero.
  - `metadata` object, nullable — Optional key-value pairs stored with the transaction. At most 5 keys. Each key matches `^[a-z0-9_]{1,30}$`, and each value is at most 200 characters.

## Response `200`

Transaction applied successfully

- VirtualCurrencyTransactionResponse — Result of a virtual currency transaction.
  - `transaction_id` string, uuid, required — Unique ID of the created transaction.
  - `balances` VirtualCurrencyBalanceSnapshot[], required — Post-transaction balance for each currency affected by this request (one entry per item in `items`). To read the full balance list for the profile, use `GET /api/v2/server-side-api/vc/balances/`.
    - `code` string, required — Virtual currency code.
    - `name` string, required — Display name of the virtual currency.
    - `balance` integer, required — Total balance, including amounts currently held.
    - `held` integer, required — Sum of all active holds (reserved amounts). Currently always 0.
    - `available` integer, required — Balance available to spend, computed as `balance - held`.

## Other responses

- `400` — Bad request. The `error_code` field identifies the cause: `insufficient_balance`, `unknown_currency`, `duplicate_currency`, `amount_zero`, `balance_overflow`, `empty_items`, or `idempotency_key_invalid`.
- `401` — Unauthorized. The API key is missing or invalid.
- `403` — Forbidden. The Server API for virtual currencies is not enabled for this app. Contact Adapty support to request access.
- `404` — Profile not found.
- `409` — Conflict. The `error_code` field identifies the cause: - `idempotency_in_flight` — a request with the same `Idempotency-Key` is still being processed. Retry after the interval in the `Retry-After` response header. - `duplicate_source_transaction` — the underlying store transaction was already credited, so it is not applied again.
- `422` — Unprocessable entity. The request body failed schema validation — for example, `items` contains more than 20 entries, or a `metadata` key or value breaks its constraints.
- `429` — Too many requests. The per-app or global rate limit was exceeded (`error_code` is `rate_limited`). The default limits are 600 requests per minute per app and 6000 requests per minute globally.
- `500` — Internal server error

---

[API](https://skmtc.dev/adapty/apis/adapty-server-side-api.md) · [All operations](https://skmtc.dev/adapty/apis/adapty-server-side-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/adapty/adapty-server-side-api/revisions/abdc1c773a17/schema)
