Skip to content

Series, subscriptions and certificates ​

Use this to sell video people watch on their own time. Creators publish single videos, series of episodes and courses. Viewers buy a video, a whole series or one episode, rent a video for a set time, or pay for a monthly plan. Your player gets a short-lived token for each video, your app tracks how far each viewer has watched, and a viewer who finishes a course earns a certificate anyone can check. Live broadcasts are the other side of Streams: see Streams and paid access.

How the pieces fit ​

Content typeWhat it is
standalone_videoOne video, with an optional free trailer.
playlistA series. Episodes in order, each with its own price and release date if you want.
courseChapters with video and text lessons. Finishing every lesson earns a certificate.

A client uses them in this order:

  1. A creator makes a draft with a proposed price, adds episodes or lessons, uploads each video and adds a trailer.
  2. The creator asks for approval. Tenant staff review the content and publish it.
  3. Your app lists the catalogue. Anyone can watch the trailer for free.
  4. A viewer buys with POST /v1/plus/orders. The purchase_type is lifetime, rental (with rental_hours) or episode. The response holds a checkout_url, and a guest only needs a name and email. A viewer can also start a plan.
  5. To play, your app asks for a playback token for the item, then reports progress as the viewer watches.
  6. When a viewer completes every required lesson in a course, a certificate is issued.

A plan costs a set price each month and covers either one organization's catalogue or every organization in the tenant. Tenant staff create plans. Each month the subscriber pays for the next cycle with POST /v1/plus/subscriptions/{subscription}/renew, which returns a checkout link. Nothing is charged automatically. A cancel ends the plan at the end of the period, or at once with at_period_end set to false.

A playback token lasts about ten minutes. The response also gives a session_id, refresh_before_expiry_seconds and heartbeat_interval_seconds. Refresh the token before it expires and send a heartbeat as often as the response says. One device can watch an item at a time. Asking for a token on a second device moves playback there, and the first device gets 403 SESSION_TAKEN_OVER on its next refresh or heartbeat. Access is checked again on every refresh and progress ping, so a refund or an ended rental stops playback. An episode before its release date returns ITEM_NOT_RELEASED.

A video counts as complete at 95 percent. Text lessons are completed with POST /v1/plus/watch/items/{item}/complete. A certificate has a serial number. The public check shows the course title and the holder's first name only.

Content prices and order amounts are decimal KES, such as 500.00. Payout and fee calls use cents.

What you can call today ​

Creators ​

MethodPathWhat it does
GET/v1/organizer/plus/contentsList your content with status counts.
POST/v1/organizer/plus/contentsCreate a draft video, series or course.
GET/v1/organizer/plus/contents/{content}Get one with its items, trailer and review notes.
PUT/v1/organizer/plus/contents/{content}Update a draft or rejected content.
DELETE/v1/organizer/plus/contents/{content}Delete a draft, rejected or retired content.
POST/v1/organizer/plus/contents/{content}/request-approvalSend it to tenant staff for review.
POST/v1/organizer/plus/contents/{content}/retireEnd it. Past purchases keep working.
GET/v1/organizer/plus/contents/{content}/itemsList episodes or lessons.
POST/v1/organizer/plus/contents/{content}/itemsAdd an episode, lesson or chapter heading.
POST/v1/organizer/plus/contents/{content}/items/reorderChange the order of items.
GET/v1/organizer/plus/contents/{content}/items/{item}Get one item.
PUT/v1/organizer/plus/contents/{content}/items/{item}Edit an item's title, price or release date.
DELETE/v1/organizer/plus/contents/{content}/items/{item}Delete an item nobody has bought.
POST/v1/organizer/plus/contents/{content}/items/{item}/uploadStart an upload for an item's video.
GET/v1/organizer/plus/contents/{content}/items/{item}/uploadCheck the upload.
DELETE/v1/organizer/plus/contents/{content}/items/{item}/uploadCancel the upload.
GET/v1/organizer/plus/contents/{content}/trailerGet the trailer and its upload state.
POST/v1/organizer/plus/contents/{content}/trailerAdd a trailer and start its upload.
POST/v1/organizer/plus/contents/{content}/trailer/replaceReplace the trailer.
DELETE/v1/organizer/plus/contents/{content}/trailerRemove the trailer.
GET/v1/organizer/plus/contents/{content}/fee-previewSee what you keep from a gross amount.
POST/v1/organizer/plus/payout-requestsAsk to be paid out. Owners only.

Viewers and the public (sign-in optional) ​

MethodPathWhat it does
GET/v1/plus/contentsBrowse the catalogue. Filter by category, type or search.
GET/v1/plus/contents/categories/topList the top categories.
GET/v1/plus/contents/{content}Get one content with its items.
GET/v1/plus/contents/{content}/trailerGet the trailer details.
POST/v1/plus/contents/{content}/trailer/playback-tokenGet a token to play the trailer.
POST/v1/plus/ordersBuy content. Returns the order and a checkout_url.
GET/v1/plus/orders/{code}Check an order by its code.
GET/v1/plus/subscription-plansList active plans.
POST/v1/plus/contents/{content}/items/{item}/playback-tokenGet a token to play an item. Send the order_code or buyer_email if the viewer is a guest.
POST/v1/plus/playback-tokens/refreshGet a fresh token for a session_id.
POST/v1/plus/playback-tokens/heartbeatKeep the session alive and learn if another device took over.
GET/v1/plus/certificates/verify/{serial}Check a certificate by its serial number.

Signed-in viewers ​

MethodPathWhat it does
POST/v1/plus/subscriptions/checkoutStart a plan. Returns a checkout_url.
POST/v1/plus/subscriptions/{subscription}/renewPay for the next month.
GET/v1/plus/my-subscriptionsList the viewer's plans.
POST/v1/plus/my-subscriptions/{subscription}/cancelCancel a plan.
GET/v1/plus/my-libraryList the viewer's active purchases.
POST/v1/plus/watch/pingReport position and time watched.
POST/v1/plus/watch/items/{item}/completeMark a text lesson done.
GET/v1/plus/watch/items/{item}/progressGet the viewer's progress on an item.
GET/v1/plus/my-certificatesList the viewer's certificates.
GET/v1/plus/my-certificates/{certificate}Get one certificate by serial number.

Tenant staff review content and manage plans from the tenant admin API. Full parameters and responses are in the API reference.

Here is a guest buying a video:

sh
curl -X POST https://api.vabu.live/v1/plus/orders \
  -H "X-Tenant-Code: $TENANT_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "content_id": "550e8400-e29b-41d4-a716-446655440001",
    "purchase_type": "lifetime",
    "customer": { "name": "Amina Wairimu", "email": "amina@example.co.ke", "phone": "+254712345678" }
  }'

The order comes back pending with a code such as PLS-A1B2-C3D4 and a checkout_url. Once the buyer pays, that code lets them play the video.

Calling these endpoints ​

The viewer and public routes marked sign-in optional take only X-Tenant-Code, and the certificate check needs nothing. GET /v1/plus/contents and GET /v1/plus/contents/{content} also accept a secret, restricted (streams:read) or publishable key. Every other route takes a signed-in user token (Authorization: Bearer ..., a Vabu ID or Payd ID token) plus X-Tenant-Code, in live mode. Creator routes also accept an optional X-Organization-Id header to pick which organization you act for. Test keys and publishable keys are refused on these routes today, apart from the two catalogue reads. See which credentials each endpoint takes.

On creator routes, any active member can read, and changes need the Owner, Admin or Editor role. For paging and error shapes, see pagination and errors.

Events you can subscribe to ​

EventWhen it fires
streams.content.purchasedA viewer paid for a video, series or episode and now has access.
access.entitlement.grantedA purchase gave a viewer access.
access.entitlement.extendedA viewer's access end date changed.
access.entitlement.revokedA viewer's access was taken away, for example after a refund.
access.entitlement.expiredA viewer's access ran out, for example a finished rental.

See events for the full payloads and webhooks for delivery and signatures.

Vabu OS documentation