Appearance
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 type | What it is |
|---|---|
standalone_video | One video, with an optional free trailer. |
playlist | A series. Episodes in order, each with its own price and release date if you want. |
course | Chapters with video and text lessons. Finishing every lesson earns a certificate. |
A client uses them in this order:
- A creator makes a draft with a proposed price, adds episodes or lessons, uploads each video and adds a trailer.
- The creator asks for approval. Tenant staff review the content and publish it.
- Your app lists the catalogue. Anyone can watch the trailer for free.
- A viewer buys with
POST /v1/plus/orders. Thepurchase_typeislifetime,rental(withrental_hours) orepisode. The response holds acheckout_url, and a guest only needs a name and email. A viewer can also start a plan. - To play, your app asks for a playback token for the item, then reports progress as the viewer watches.
- 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
| Method | Path | What it does |
|---|---|---|
| GET | /v1/organizer/plus/contents | List your content with status counts. |
| POST | /v1/organizer/plus/contents | Create 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-approval | Send it to tenant staff for review. |
| POST | /v1/organizer/plus/contents/{content}/retire | End it. Past purchases keep working. |
| GET | /v1/organizer/plus/contents/{content}/items | List episodes or lessons. |
| POST | /v1/organizer/plus/contents/{content}/items | Add an episode, lesson or chapter heading. |
| POST | /v1/organizer/plus/contents/{content}/items/reorder | Change 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}/upload | Start an upload for an item's video. |
| GET | /v1/organizer/plus/contents/{content}/items/{item}/upload | Check the upload. |
| DELETE | /v1/organizer/plus/contents/{content}/items/{item}/upload | Cancel the upload. |
| GET | /v1/organizer/plus/contents/{content}/trailer | Get the trailer and its upload state. |
| POST | /v1/organizer/plus/contents/{content}/trailer | Add a trailer and start its upload. |
| POST | /v1/organizer/plus/contents/{content}/trailer/replace | Replace the trailer. |
| DELETE | /v1/organizer/plus/contents/{content}/trailer | Remove the trailer. |
| GET | /v1/organizer/plus/contents/{content}/fee-preview | See what you keep from a gross amount. |
| POST | /v1/organizer/plus/payout-requests | Ask to be paid out. Owners only. |
Viewers and the public (sign-in optional)
| Method | Path | What it does |
|---|---|---|
| GET | /v1/plus/contents | Browse the catalogue. Filter by category, type or search. |
| GET | /v1/plus/contents/categories/top | List the top categories. |
| GET | /v1/plus/contents/{content} | Get one content with its items. |
| GET | /v1/plus/contents/{content}/trailer | Get the trailer details. |
| POST | /v1/plus/contents/{content}/trailer/playback-token | Get a token to play the trailer. |
| POST | /v1/plus/orders | Buy content. Returns the order and a checkout_url. |
| GET | /v1/plus/orders/{code} | Check an order by its code. |
| GET | /v1/plus/subscription-plans | List active plans. |
| POST | /v1/plus/contents/{content}/items/{item}/playback-token | Get a token to play an item. Send the order_code or buyer_email if the viewer is a guest. |
| POST | /v1/plus/playback-tokens/refresh | Get a fresh token for a session_id. |
| POST | /v1/plus/playback-tokens/heartbeat | Keep 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
| Method | Path | What it does |
|---|---|---|
| POST | /v1/plus/subscriptions/checkout | Start a plan. Returns a checkout_url. |
| POST | /v1/plus/subscriptions/{subscription}/renew | Pay for the next month. |
| GET | /v1/plus/my-subscriptions | List the viewer's plans. |
| POST | /v1/plus/my-subscriptions/{subscription}/cancel | Cancel a plan. |
| GET | /v1/plus/my-library | List the viewer's active purchases. |
| POST | /v1/plus/watch/ping | Report position and time watched. |
| POST | /v1/plus/watch/items/{item}/complete | Mark a text lesson done. |
| GET | /v1/plus/watch/items/{item}/progress | Get the viewer's progress on an item. |
| GET | /v1/plus/my-certificates | List 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
| Event | When it fires |
|---|---|
streams.content.purchased | A viewer paid for a video, series or episode and now has access. |
access.entitlement.granted | A purchase gave a viewer access. |
access.entitlement.extended | A viewer's access end date changed. |
access.entitlement.revoked | A viewer's access was taken away, for example after a refund. |
access.entitlement.expired | A viewer's access ran out, for example a finished rental. |
See events for the full payloads and webhooks for delivery and signatures.
Related
- Streams for live broadcasts.
- Paid access for the access model behind these purchases.
- Payments for checkout and payouts.
- Fanbases for the community around a creator.