VipterHelp Center

API conventions

The base URL, the version header, the request and response format, how money, dates and IDs are represented, the error table, idempotency, pagination, rate limits, the Request-Id header and what differs from Stripe.

Admin or OwnerAll plans

The Vipter API follows the conventions of Stripe's API wherever there is an equivalent: field names, error format, cursor pagination, idempotency and date-based versions. Anyone who has integrated Stripe will recognize everything. This page gathers what applies to every endpoint; each endpoint is in the reference.

Base URL

https://api.vipter.com/v1

The same service answers at https://app.vipter.com/api/v1, with the same paths. Use api.vipter.com in new integrations; the other address exists for networks where only the dashboard's domain is allowed.

Every call is HTTPS. A URL outside /v1 or a path that does not exist gets 404 resource_missing, in the same format as the other errors.

Version

The API has date-based versions. The current version is 2026-11-01, the only one that exists today.

Each API key is born pinned to the current version on the day it was created, and requests with it use that version without saying anything. To ask for another version on a call, send the header:

Vipter-Version: 2026-11-01

An unknown version gets 400 invalid_api_version, with the list of accepted versions in the message. Every authenticated response carries the Vipter-Version header with the version used.

Within a version, the API only changes in compatible ways: new fields may appear in any object at any time, and new values may appear in text fields such as status and payment_method. Write your code to ignore fields it does not know. An incompatible change, such as renaming or removing a field, becomes a new version, and existing keys stay on the old one.

Request format

Query parameters go in the query string. Request bodies, on the endpoints that take one, may be JSON or a form:

Content-TypeExample
application/json{"metadata": {"plan": "pro"}, "tags": ["a", "b"]}
application/x-www-form-urlencodedmetadata[plan]=pro&tags[]=a&tags[]=b

The form format uses Stripe's bracket notation: a[b]=1 becomes an object, a[0]=x&a[1]=y or a[]=x&a[]=y become a list. A curl copied from Stripe's documentation works as is. In a form every value arrives as text; the API converts numbers and booleans where it expects them.

The query string accepts the same bracket notation. A body with another Content-Type, or JSON that is not an object, gets 400 parameter_invalid.

Response format

Every response is JSON in UTF-8, with Cache-Control: no-store. Retrieving an object returns the object; a listing returns a list object (see Pagination); an error returns an error object (see Errors).

Headers present on every response:

HeaderContent
Request-IdUnique identifier of the request, req_ followed by 20 hexadecimal characters. See Request-Id.
Vipter-VersionThe API version used. Only on authenticated responses.
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-ResetThe state of your rate limit.
Retry-AfterOnly on 429: seconds until you may try again.
Idempotent-ReplayedOnly when the response is the replay of an earlier request with the same Idempotency-Key. See Idempotency.

Data types

WhatHow it comesExample
MoneyInteger in the currency's minor unit. Never a decimal.9900 is R$ 99.00; 1250 is US$ 12.50.
CurrencyLowercase ISO 4217 code."brl", "usd"
Date and timeInteger in Unix seconds, UTC. null when it does not apply.1790790000
IDsText with a prefix per object type. Treat as opaque: do not depend on the length or the format.cust_…, sub_…, ord_…, ofr_…, prd_…, cs_…, bps_…, ak_…, req_…
objectOn every object: the type's name."customer", "subscription", "order", "offer", "product", "checkout.session", "billing_portal.session", "account", "list"
livemodeOn every object. On orders, false when the payment went through a provider's test connection. On the other objects, true in production.true
createdOn every object: when it was created, in Unix seconds.1790790000
AbsenceField present with null, not an omitted field. Empty lists come as [], empty maps as {}."phone": null
CountryUppercase ISO 3166-1 alpha-2 code."BR"
DocumentsOnly the type and the last four digits. The API never returns a full CPF or CNPJ (Brazilian tax IDs).{"type": "cpf", "number_masked": "*******1234"}

The metadata, client_reference_id and checkout_session fields of orders and subscriptions come filled in when the sale was born from a checkout session created through the API; on other sales, they come {} or null.

Errors

An error is a response with a 4xx or 5xx HTTP status and this body:

{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "Invalid query parameter 'limit': Number must be less than or equal to 100",
    "param": "limit",
    "doc_url": "https://docs.vipter.com/pt-br/developers/api-conventions#erros"
  }
}
FieldContent
typeThe error family. Decides how your code should react.
codeThe exact reason, stable across versions. Use it to handle specific cases.
messageEnglish text for whoever is debugging. It may change; do not compare against it.
paramThe parameter or header that caused the error, when there is one. In nested bodies it uses dots and brackets: lines[0].amount.
doc_urlThis section.

Error types

HTTPtypeWhen
400invalid_request_errorMissing or invalid parameter, malformed body, unknown version.
401authentication_errorKey missing, invalid, revoked or expired.
403permission_errorKey without the required scope, API switched off for the store, inactive store.
404invalid_request_errorThe object or the path does not exist. code is resource_missing.
400 or 409idempotency_errorProblem with the Idempotency-Key.
429rate_limit_errorRate limit reached.
402card_errorReserved for charge refusals on the payment endpoints, coming soon.
500api_errorFailure on Vipter's side. Keep the Request-Id and try again.

Codes

codeHTTPMeaning
parameter_missing400A required parameter was not sent. param says which.
parameter_invalid400A parameter came with the wrong value, type or format. param says which.
invalid_api_version400The Vipter-Version header has an unknown version.
api_key_missing, invalid_api_key, api_key_revoked, api_key_expired401See Authentication errors.
api_not_enabled, project_inactive, insufficient_scope403See Authentication errors.
resource_missing404There is no object with that ID in the store, or the path does not exist. param is id, or starting_after/ending_before when the cursor does not exist.
idempotency_key_too_long400The Idempotency-Key is longer than 255 characters.
idempotency_key_reused400The same Idempotency-Key was used with another method, path or body.
idempotency_key_in_use409The first request with that Idempotency-Key is still being processed.
rate_limit429See Rate limits.
internal_error500Failure on Vipter's side.

An object that exists but belongs to another store is a 404, the same as a nonexistent ID. The codes specific to one endpoint, such as offer_unavailable or portal_disabled, are in the reference, next to the endpoint.

Idempotency

Repeating a request because of a network timeout must not charge twice. For that, every POST and DELETE accepts the Idempotency-Key header, with a unique value you generate, such as a UUID v4:

curl -X POST https://api.vipter.com/v1/… \
  -H "Authorization: Bearer vk_live_…" \
  -H "Idempotency-Key: 5f0b2c8e-3a1d-4e7f-9b6c-2d4a8e1f0c3b" \
  …

The rules, the same as Stripe's:

  • The key is valid for 24 hours within the store. Up to 255 characters.
  • The same key with the same method, path and body returns the stored response from the first time, with the same HTTP status, without executing anything again. The replayed response carries Idempotent-Replayed: true and Original-Request-Id with the first request's Request-Id.
  • The same key with a different body gets 400 idempotency_key_reused.
  • While the first request is still running, a second one with the same key gets 409 idempotency_key_in_use. Wait a moment and repeat with the same key.
  • 4xx responses are stored and replayed too. 5xx responses are not: you may retry with the same key.

GET endpoints are idempotent by nature and ignore the header. On today's POST endpoints (checkout sessions, customers, portal sessions) the key is optional and recommended; on the charge endpoints that come next, it will be mandatory.

Pagination

Every listing returns a list object:

{
  "object": "list",
  "url": "https://api.vipter.com/v1/orders",
  "has_more": true,
  "data": [
    { "id": "ord_7b3e9f1c2a8d4e6f", "object": "order", "created": 1790790412 },
    { "id": "ord_5c1e8a2b9d4f4e7a", "object": "order", "created": 1790704011 }
  ]
}

Items come newest first. Parameters, the same as Stripe's:

ParameterContent
limitHow many items per page, from 1 to 100. Default 10.
starting_afterThe id of the last item on the current page. Returns the items older than it: the next page.
ending_beforeThe id of the first item on the current page. Returns the items newer than it: the previous page.

Do not send both cursors in the same call (400 parameter_invalid). A cursor that does not exist in the store gets 404 resource_missing, with param saying which. The endpoint's filters, such as customer or status, keep applying with the cursor.

To walk through everything, repeat while has_more is true, passing the last item's id in starting_after:

curl "https://api.vipter.com/v1/orders?limit=100" \
  -H "Authorization: Bearer vk_live_…"

# has_more: true, last id: ord_5c1e8a2b9d4f4e7a
curl "https://api.vipter.com/v1/orders?limit=100&starting_after=ord_5c1e8a2b9d4f4e7a" \
  -H "Authorization: Bearer vk_live_…"

Offers come in alphabetical order by name, not by date, because they are a small catalog. The cursors work the same way.

Rate limits

Each API key may make 100 requests every 2 seconds, which is 50 per second with room for bursts. Every response says where you stand:

HeaderContent
X-RateLimit-LimitThe window size: 100.
X-RateLimit-RemainingHow many requests still fit in the current window.
X-RateLimit-ResetWhen the window reopens, in Unix seconds.

Past the limit, the answer is 429 rate_limit with Retry-After in seconds. Wait that long and repeat. In an integration that sweeps a lot of data, use limit=100 and a small pause between pages instead of parallelizing calls.

Requests without a valid key have a separate limit, per IP address: 10 per minute.

Request-Id

Every response, including errors and 429, carries a unique Request-Id:

Request-Id: req_8c2f4e6a1b3d5f7e9a0c

Record this value in your logs next to the call. When talking to Vipter support about a request, give the Request-Id: with it, support finds the exact call, with its status, duration and error. The same value appears in the dashboard's request logs, which are kept for 30 days.

Differences from Stripe

The API is not a clone: the goal is for a Stripe integration to be adapted by mapping, not for Stripe's SDK to work pointed at Vipter. What differs:

TopicStripeVipter
Keysk_live_… and sk_test_…Only vk_live_…. There is no test mode; test with a test connection and use livemode per object.
VersionStripe-VersionVipter-Version, in the same date format.
Request bodyForm onlyForm or JSON.
Invoiceinvoiceorder, with billing_reason to say whether it is a one-off purchase, a first charge, a renewal, a manual charge or usage.
Priceprice, one per product and currencyoffer, with a prices[] list, one per currency, and a ready checkout_url.
Subscriptionitems[] with several pricesOne offer per subscription: offer and product are one object each.
Checkout sessionline_items[] with several pricesOne offer per session: offer (or line_items[0][price]), with pack for the quantity. Coupons go in discounts[0][coupon], as in Stripe.
Subscription statusincomplete, unpaidDo not exist. past_due is the subscription in payment recovery; the details come in past_due_details.
Order with manual statusDoes not existstatus_source says whether the status came from the provider or was set in the dashboard.
expand[]Expands related objectsDoes not exist. Related objects come as an id, or as {id, name} when the name helps.
WebhooksStripe-SignatureVipter-Signature, with the same algorithm. See Verify the signature.

What to do next

Was this page helpful?

On this page

Language