---
name: vipter-api
description: Integrate a SaaS, ERP or custom system with a Vipter store through its REST API (Stripe-style conventions). Use when the task mentions Vipter, vk_live_ keys, api.vipter.com, Vipter checkout sessions, subscription charges, Billing Meters on Vipter, or migrating billing code from Stripe to Vipter.
license: Proprietary. Free to use with Vipter stores.
metadata:
  version: "2026-11-01"
  docs: https://docs.vipter.com/pt-br/developers/overview
  openapi: https://api.vipter.com/v1/openapi.json
  postman: https://api.vipter.com/v1/postman.json
  llms: https://docs.vipter.com/llms.txt
---

# Vipter API

Vipter is a hosted checkout and subscription platform for Brazil and Latin America (PIX, cards with installments, boleto
where the acquirer supports it). Stores connect their own payment providers; the API follows Stripe's conventions, so
Stripe habits transfer. This skill tells you how to call it correctly and where the differences from Stripe are.

## Before you start

1. Ask for an API key (`vk_live_` + 44 characters) created by the merchant in **Settings › Developers**. Never invent or
   guess one; never print it back. Read it from an environment variable such as `VIPTER_API_KEY`.
2. Base URL: `https://api.vipter.com/v1` (the same routes answer at `https://app.vipter.com/api/v1`).
3. First call, to confirm the key and learn the store's currency and timezone:
   `GET /v1/account` → `{ object: "account", name, currency, timezone, api_key: { scopes } }`.
4. There is no test mode and no `vk_test_` key. Tests run against the real store with a **test connection** of the
   payment provider (Pagar.me, Mercado Pago, Asaas sandbox); orders paid through it carry `livemode: false`.

## Conventions (same as Stripe unless noted)

- Auth: `Authorization: Bearer vk_live_…` (HTTP Basic with the key as user also works).
- Version: header `Vipter-Version: 2026-11-01`; defaults to the version pinned on the key.
- Money in minor units (`1990` = R$ 19,90); currency lowercase (`brl`); dates as Unix seconds.
- Lists: `{ object: "list", data, has_more, url }`, cursor pagination with `limit`, `starting_after`, `ending_before`.
- Errors: `{ error: { type, code, message, param, doc_url } }`; types `invalid_request_error`, `authentication_error`,
  `permission_error`, `idempotency_error`, `rate_limit_error`, `card_error` (HTTP 402).
- `Idempotency-Key` on any `POST` (24 h replay, header `Idempotent-Replayed: true`); **required** on subscription charges.
- Rate limit: 100 requests per 2 s per key (`X-RateLimit-*` headers, 429 on excess).
- Bodies: JSON or `application/x-www-form-urlencoded` with Stripe bracket notation (`metadata[plan]=pro`).
- Every response has a `Request-Id`; quote it when reporting a problem.

## Objects and their Stripe counterparts

| Vipter | Prefix | Stripe |
|---|---|---|
| `product` | `prd_` | Product |
| `offer` (price per currency, billing cycle, trial, pack) | `ofr_` | Price |
| `customer` | `cust_` | Customer |
| `subscription` (`status`: trialing, active, past_due, paused, canceled, expired) | `sub_` | Subscription |
| `order` (every charge: purchase, renewal, extra; `billing_reason`) | `ord_` | Invoice (+ PaymentIntent) |
| `subscription_charge` (off-cycle charge on the saved card) | `sch_` | InvoiceItem + Invoice paid now |
| `checkout.session` | `cs_` | Checkout Session |
| `billing_portal.session` | — | Billing Portal Session |
| `billing.meter`, `billing.meter_event`, `usage_item`, `usage_period` | `mtr_`, `mev_`, `usi_`, `usp_` | Billing Meter, Meter Event, Price (metered) |
| `webhook_endpoint` (`secret` `whsec_…`, header `Vipter-Signature`) | — | Webhook Endpoint |
| `event` | `evt_` | Event |

Not available: PaymentIntents, PaymentMethods, Prices with `price_data` inline, `expand[]`, Search API, tax rates,
multiple line items per session, importing cards from another provider.

## Workflows

### Sell a plan to a user of a SaaS

1. Find the offer: `GET /v1/offers?type=recurring&active=true` → pick `id` (`ofr_…`).
2. `POST /v1/checkout/sessions` with `offer`, `client_reference_id` (the SaaS user id), `customer_email`,
   `metadata`, `success_url` containing `{CHECKOUT_SESSION_ID}`, `cancel_url`. Send an `Idempotency-Key`.
3. Redirect the browser to the returned `url`. The buyer pays on Vipter's page and is sent back to `success_url`.
4. Confirm with `GET /v1/checkout/sessions/{id}` (`status: complete`, `payment_status: paid`, `subscription`,
   `customer`, `order`) **and** handle the webhook `checkout.session.completed`. PIX stays `payment_status: pending`
   until paid, then `checkout.session.async_payment_succeeded` fires.
5. `client_reference_id` and `metadata` are copied to the order and the subscription, so later events identify the user.

### Charge an extra amount on a subscription (usage, overage, add-on)

`POST /v1/subscriptions/{id}/charges` with `amount` (minor units, at least the currency minimum, 100 for BRL),
optional `description`, `lines[]` (`quantity × unit_amount` must add up to `amount`) and `metadata`.
`Idempotency-Key` is mandatory. 201 returns the `subscription_charge` with `status: succeeded|pending|failed` and the
`order`; a card refusal is `402 card_error` with `error.subscription_charge`; below the currency minimum is
`400 amount_too_small`. Cards stored at Mercado Pago cannot be charged off-cycle (`payment_method_not_chargeable`). Events: `subscription_charge.succeeded|failed`, `invoice.paid|payment_failed`.

### Metered billing without keeping the tally yourself

1. `POST /v1/billing/meters` `{ display_name, event_name, default_aggregation: { formula: "sum"|"count"|"last" } }`.
2. Price it on the offer (dashboard) or per subscription: `POST /v1/subscriptions/{id}/usage_items`
   `{ meter, unit_amount, included_units, tiers?, billing_threshold? }`.
3. Report usage: `POST /v1/billing/meter_events` `{ event_name, payload: { customer_id, value }, identifier }` or
   `/batch` with up to 100. `identifier` makes it idempotent; `timestamp` may be up to 35 days in the past.
4. Vipter sums the period, subtracts the allowance and charges at the cycle end (or when the threshold is reached);
   read `GET /v1/subscriptions/{id}/usage` and `GET /v1/billing/meters/{id}/event_summaries?customer=&start_time=&end_time=`.

### Manage subscriptions

`POST /v1/subscriptions/{id}` (`client_reference_id`, `metadata`, `cancel_at_period_end`), `DELETE /v1/subscriptions/{id}`
(cancel now), `POST …/pause`, `…/resume`, `…/reactivate`, `…/change_offer` `{ offer }` (same product family).
"Manage subscription" button: `POST /v1/billing_portal/sessions` `{ customer, return_url }` → one-time `url`.

### Receive events

`POST /v1/webhook_endpoints` `{ url, enabled_events: ["checkout.session.*", "invoice.paid"], api_version: "2026-11-01" }`
returns `secret` once. Verify `Vipter-Signature: t=…,v1=…` as HMAC SHA-256 of `` `${t}.${rawBody}` `` with the secret
(same scheme as Stripe; a Stripe verifier works if you swap header and secret). Answer 2xx within 10 s; deliveries
retry for 72 h. Deduplicate by `(provider, event.id)`. Test with `POST /v1/webhook_endpoints/{id}/test`. Catalog
`2026-11-01` uses Stripe names (`checkout.session.completed`, `customer.subscription.updated` with
`data.previous_attributes`, `invoice.paid`); catalog `2026-09-01` keeps Vipter's original names (`order.paid`).

## Pitfalls

- `amount` on a checkout session comes from the offer; you cannot pass an arbitrary price. Create an offer instead.
- `invoice.paid` carries an `order` object (`object: "order"`), not an invoice; the subscription's `offer` replaces `price`.
- `customer.subscription.updated` covers renewals, plan changes and dunning recovery; read `previous_attributes`.
- One line item per session (`line_items[0].price` is an alias of `offer`; `quantity` means pack units).
- Only `write` keys can `POST`/`DELETE`; `read` keys get `403 insufficient_scope`.
- `403 api_not_enabled` means the merchant's store was switched off by Vipter; `403 project_inactive` the store is
  suspended. Both are for the merchant to resolve, not for code.

## References

- Full reference with every field: https://docs.vipter.com/pt-br/developers/api-reference (append `.md` for Markdown; `/en/` and `/es/` exist).
- SaaS guide with code: https://docs.vipter.com/pt-br/developers/saas-quickstart.md
- Migrating from Stripe: https://docs.vipter.com/pt-br/developers/stripe-migration.md
- Metered usage: https://docs.vipter.com/pt-br/developers/usage-metering.md
- Events: https://docs.vipter.com/pt-br/developers/events.md · Signatures: https://docs.vipter.com/pt-br/developers/signatures.md
- OpenAPI 3.1: https://api.vipter.com/v1/openapi.json · Postman: https://api.vipter.com/v1/postman.json
