Skip to content

Paid access ​

You sell the pass in your own checkout, with your own payment provider. Vabu decides who gets to press play. The link between the two is one call from your server: grant an entitlement after the payment clears.

An entitlement says that one customer may use one resource, for a period, within limits. The resource is a live stream (type ls) or an asset (type ast).

Grant access after a payment ​

Make this call from your server when your payment provider confirms the money. Use a secret key, never a browser key.

http
POST /v1/access/entitlements
Authorization: Bearer vk_live_...
Idempotency-Key: grant-pay-8842

{
  "customer": "user_42",
  "resource": { "type": "ls", "id": "ls_01J9ZQ4M8T3K6Y2E5W7N0B1C9D" },
  "source": "tenant_grant",
  "source_ref": "pay-8842",
  "starts_at": "2026-10-16T16:00:00Z",
  "ends_at": "2026-10-17T04:00:00Z",
  "limits": {
    "max_devices": 2,
    "max_concurrent_sessions": 1,
    "concurrency_policy": "replace_oldest"
  },
  "metadata": { "ticket": "VIP" }
}

You get 201 and an entitlement with "status": "active".

FieldMeaning
customerYour own id for the buyer. It must match the sub of the customer token you sign for them. If you have no accounts, send contact_hash instead: a hash of a verified phone or email, salted with your own secret.
resourceWhat they may watch. ls for a live stream, ast for a video asset.
sourceWhy they have access, for your reports: tenant_grant (the default), payment, comp, subscription, membership_tier, ticket or promotion.
source_refThe payment, order or grant behind it. Your payment id is a good value.
starts_at, ends_atThe window. Both need a time zone offset, such as Z. Leave them out for no limit.
limitsDevice and viewing rules. See below.
external_id, metadataYour own references.

Make it safe to repeat ​

Payment providers send the same confirmation more than once, so your handler will run more than once. This is safe. Two things protect you:

  • The Idempotency-Key header is required. Send the same key and you get the stored response back.
  • With a source_ref, a second grant for the same source, reference, customer and resource changes nothing and returns the existing entitlement with 200.

A repeat of a revoked grant is refused with 409 entitlement_revoked. If you want to give access again, use a new source_ref.

Check before you show a button ​

POST /v1/access/check answers whether a customer may watch right now:

json
{ "data": { "allowed": false, "reason": "expired", "entitlement": { "id": "ent_...", "status": "expired" } } }

reason is null when allowed. Otherwise it is no_entitlement, expired, revoked, suspended, not_started, country_not_allowed or domain_not_allowed. Use it to choose between "Watch" and "Buy a pass". The answer follows every change at once, including a revoke.

Limit devices ​

The limits object decides what a second screen does.

You wantSet
One screen at a time, the latest wins (a viewer who picks up their phone moves the stream there)max_concurrent_sessions: 1, concurrency_policy: "replace_oldest"
One screen at a time, the first holds the seatmax_concurrent_sessions: 1, concurrency_policy: "reject_new"
A household with up to 2 devices in allmax_devices: 2
A pass that works for 3 viewingsmax_views: 3
Only viewers in Kenya and Ugandaallowed_countries: ["KE", "UG"]
Only on your own siteallowed_domains: ["example.com"]

Your page sends a device_id when it opens a session. Use one stable id for each browser or phone, for example one saved in local storage. Vabu counts different ids as different devices.

With replace_oldest, the older session ends at its next heartbeat, within 30 seconds, and its player gets 403 session_ended with the reason replaced_by_newer_session. Show "You are watching on another device". With reject_new, the second device is refused with 409 concurrency_limit_reached.

How sessions and heartbeats work is in playback and sessions.

Revoke access ​

Take access away when a payment is refunded or reversed:

http
DELETE /v1/access/entitlements/ent_...

This is final. The response is the entitlement with "status": "revoked". For a pause you can undo, use POST /v1/access/entitlements/{id}/suspend and .../resume.

What happens next:

  • The next check says no, at once.
  • A new session is refused at once.
  • A session that is playing ends at its next heartbeat.
  • The playback link already in the viewer's player works until its token runs out, at most about 11 minutes. See signed playback.

Find a customer's entitlements with GET /v1/access/entitlements?customer=user_42, or by your own id with GET /v1/access/entitlements/external/{external_id}.

Sell replays ​

A replay is an asset, so it takes an entitlement of its own, with resource type ast.

  1. Add the recording as an asset. See assets and recordings. Wait for streams.asset.ready.
  2. When someone buys the replay, grant an entitlement on the asset, with an ends_at that matches what you sold, for example 7 days.
  3. To include the replay in a live pass, grant two entitlements in the same handler: one on the stream and one on the asset, with a later ends_at on the asset.

The viewer opens a session on the asset exactly as on a live stream. Because the replay entitlement has its own limits, you can sell a replay at one screen per purchase.

Tickets that include a stream ​

If you sell tickets with Vabu Events, listen for events.order.paid and grant the stream in your handler, with source: "ticket", the order code as source_ref and your own id for the buyer. See how the products work together.

Keep your records in step ​

Every grant, revoke, suspend, resume and expiry sends an event: access.entitlement.granted, revoked, suspended, resumed, expired and extended. Use them to update your own database. See stream events.

Vabu OS documentation