Payments
Vidivo uses a platform-managed payment model. The platform collects all payments directly via Stripe. Hosts add bank accounts and request payouts, which are transferred via Stripe Payouts API. A 7% platform fee is deducted from each payment.
Time-Window Billing Model
Section titled “Time-Window Billing Model”The billing model pre-authorizes funds before they are spent, preventing unpayable debts at call end.
How It Works
Section titled “How It Works” Time: 0 10 20 30 min | | | | v v v v Holds: [--W1----][--W2----][--W3---] ^ ^ W1 captured W2 captured
At call end (e.g. 24 min): +-- W3 partial: 4 min x rate = captured +-- Remainder of W3 hold: released- Guest submits payment method. Stripe hold placed for Window 1 (
window_size x rate). - At
window_size - 60 seconds, hold for Window 2 is placed. - At the
window_sizeboundary, Window 1 is captured (charged permanently). - This repeats for every subsequent window.
- When the call ends, the partial final window is captured at
ceil(elapsed_minutes) x rateand remaining holds are released.
Example
Section titled “Example”Host rate: $3.00/min, window size: 10 minutes.
| Time | Action | Stripe Operation |
|---|---|---|
| T+0s | Call starts | Hold $30.00 (W1) |
| T+9m | 60s before W1 ends | Hold $30.00 (W2) |
| T+10m | W1 boundary | Capture $30.00 (W1) |
| T+19m | 60s before W2 ends | Hold $30.00 (W3) |
| T+20m | W2 boundary | Capture $30.00 (W2) |
| T+23m30s | Guest ends call | Capture $12.00 (4 min rounded up x $3), Release $18.00 |
Total charged: $72.00 | Host receives: $72.00 x 0.93 = $66.96
POST /v1/payments/setup-intent
Section titled “POST /v1/payments/setup-intent”Create a Stripe SetupIntent for saving a card for future payments.
Request
Section titled “Request”POST /v1/payments/setup-intentContent-Type: application/json{ "email": "alex@example.com"}Response
Section titled “Response”HTTP/1.1 201 Created{ "client_secret": "seti_1OqBuv2eZvKYlo2C_secret_XXXX"}POST /v1/payments/guest/setup-intent
Section titled “POST /v1/payments/guest/setup-intent”Create a Stripe SetupIntent for a guest caller. Returns the client secret and the Stripe publishable key needed to initialize Stripe.js on the frontend.
Request
Section titled “Request”POST /v1/payments/guest/setup-intentContent-Type: application/json{ "email": "guest@example.com"}Response
Section titled “Response”HTTP/1.1 201 Created{ "client_secret": "seti_1OqBuv2eZvKYlo2C_secret_XXXX", "publishable_key": "pk_live_XXXX"}GET /v1/payments/history
Section titled “GET /v1/payments/history”Retrieve payment history for the authenticated user. Hosts see earnings; guests see charges.
Authentication: Bearer token required.
Request
Section titled “Request”GET /v1/payments/history?page=1&per_page=20&start_date=2026-03-01&end_date=2026-03-31&status=succeededAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...| Query Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 20) |
start_date | string | Filter: only transactions after this date (YYYY-MM-DD) |
end_date | string | Filter: only transactions before this date (YYYY-MM-DD) |
status | string | Filter by status |
Response
Section titled “Response”{ "success": true, "data": [ { "id": "01JN2X8O2R7J1UDVY0AB1BFMJK", "call_session_id": "01JN2X6M0P5H9SRBTXY8ZDKEFG", "window_number": 1, "type": "capture", "amount": 30.00, "currency": "usd", "status": "succeeded", "created_at": "2026-03-15T14:10:00Z" } ], "pagination": { "page": 1, "per_page": 20, "total": 5, "total_pages": 1 }}GET /v1/payments/history/export
Section titled “GET /v1/payments/history/export”Export payment history as CSV, XLSX, or PDF.
Authentication: Bearer token required.
GET /v1/payments/history/export?format=csv&start_date=2026-03-01&end_date=2026-03-31Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...| Query Parameter | Type | Description |
|---|---|---|
format | string | csv (default), xlsx, or pdf |
start_date | string | Filter start date (YYYY-MM-DD) |
end_date | string | Filter end date (YYYY-MM-DD) |
status | string | Filter by status |
Returns a file download with appropriate Content-Type and Content-Disposition headers.
GET /v1/payments/earnings
Section titled “GET /v1/payments/earnings”Retrieve earnings summary for the authenticated host.
Authentication: Bearer token required (role: host or admin).
GET /v1/payments/earningsAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Response
Section titled “Response”{ "total_earned": 1250.00, "total_paid_out": 980.00, "pending_balance": 270.00, "currency": "usd", "total_calls": 47, "total_minutes": 623}GET /v1/payments/earnings/chart
Section titled “GET /v1/payments/earnings/chart”Retrieve daily aggregated earnings data for charting.
Authentication: Bearer token required (role: host or admin).
GET /v1/payments/earnings/chart?period=30dAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...| Query Parameter | Type | Description |
|---|---|---|
period | string | 7d, 30d (default), or 90d |
Response
Section titled “Response”{ "period": "30d", "data": [ { "date": "2026-03-01", "amount": 45.00 }, { "date": "2026-03-02", "amount": 32.50 }, { "date": "2026-03-03", "amount": 0 } ]}POST /v1/payments/bank-account
Section titled “POST /v1/payments/bank-account”Add a bank account for receiving payouts. Creates a Stripe Custom connected account.
Authentication: Bearer token required (role: host or admin).
Request
Section titled “Request”POST /v1/payments/bank-accountAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Content-Type: application/json{ "bank_token": "btok_1XXXXXXXXXXXXXXXXX"}| Field | Type | Required | Description |
|---|---|---|---|
bank_token | string | Yes | Stripe bank account token from Stripe.js |
Response
Section titled “Response”HTTP/1.1 201 Created{ "bank_name": "Chase", "bank_last_four": "6789", "payout_enabled": true}GET /v1/payments/bank-account
Section titled “GET /v1/payments/bank-account”Retrieve the host’s saved bank account information.
Authentication: Bearer token required (role: host or admin).
GET /v1/payments/bank-accountAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Response
Section titled “Response”{ "bank_name": "Chase", "bank_last_four": "6789", "payout_enabled": true}POST /v1/payments/payout
Section titled “POST /v1/payments/payout”Request a payout to the host’s bank account.
Authentication: Bearer token required (role: host or admin).
Request
Section titled “Request”POST /v1/payments/payoutAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Content-Type: application/json{ "amount": 100.00}| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Payout amount in USD |
Response
Section titled “Response”HTTP/1.1 201 Created{ "id": "01JN2X9P3S8K2VEWZ1BC2CGNLM", "amount": 100.00, "currency": "usd", "status": "pending", "created_at": "2026-03-15T14:00:00Z"}Errors
Section titled “Errors”| Code | Status | Description |
|---|---|---|
validation_error | 400 | Amount exceeds available balance |
forbidden | 403 | Payouts not enabled (bank account not added) |
stripe_error | 402 | Stripe payout creation failed |
GET /v1/payments/payouts
Section titled “GET /v1/payments/payouts”Retrieve payout history for the host.
Authentication: Bearer token required (role: host or admin).
GET /v1/payments/payouts?page=1&per_page=20&start_date=2026-03-01&end_date=2026-03-31Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...| Query Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 20) |
start_date | string | Filter start date |
end_date | string | Filter end date |
status | string | Filter by status |
GET /v1/payments/payouts/export
Section titled “GET /v1/payments/payouts/export”Export payout history as CSV, XLSX, or PDF.
Authentication: Bearer token required (role: host or admin).
GET /v1/payments/payouts/export?format=xlsxAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...POST /v1/payments/connect/onboard
Section titled “POST /v1/payments/connect/onboard”Create a Stripe Custom connected account and return an onboarding URL where the host completes identity verification and bank account setup.
Authentication: Bearer token required (role: host or admin).
Request
Section titled “Request”POST /v1/payments/connect/onboardAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Content-Type: application/json{ "refresh_url": "https://vidivo.app/settings/payments", "return_url": "https://vidivo.app/settings/payments?onboarded=true"}Response
Section titled “Response”HTTP/1.1 201 Created{ "onboarding_url": "https://connect.stripe.com/setup/e/acct_XXXX/..."}GET /v1/payments/invoices/:id/download
Section titled “GET /v1/payments/invoices/:id/download”Download an invoice PDF for a captured payment. Returns a presigned URL to the invoice file stored in Cloudflare R2.
Authentication: Bearer token required.
GET /v1/payments/invoices/01JN2X8O2R7J1UDVY0AB1BFMJK/downloadAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Response
Section titled “Response”{ "download_url": "https://r2.vidivo.app/invoices/2026/03/01JN2X8O2R7J1UDVY0AB1BFMJK.pdf?sig=..."}POST /v1/stripe/webhook
Section titled “POST /v1/stripe/webhook”Stripe webhook endpoint. Receives and processes Stripe events. The endpoint verifies the Stripe-Signature header using the webhook signing secret.
Authentication: None (verified via Stripe signature).
Handled Events
Section titled “Handled Events”| Event | Description |
|---|---|
payment_intent.succeeded | Payment hold or capture succeeded |
payment_intent.payment_failed | Payment failed --- triggers notification |
payout.paid | Host payout sent to bank |
payout.failed | Host payout failed --- triggers notification |
Code Examples
Section titled “Code Examples”# Get payment history with date filtercurl "https://api.vidivo.app/v1/payments/history?start_date=2026-03-01&end_date=2026-03-31" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."
# Export payments as CSVcurl "https://api.vidivo.app/v1/payments/history/export?format=csv" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \ -o payments.csv
# Get earnings summarycurl https://api.vidivo.app/v1/payments/earnings \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."
# Request payoutcurl -X POST https://api.vidivo.app/v1/payments/payout \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \ -H "Content-Type: application/json" \ -d '{"amount": 100.00}'
# Download invoicecurl https://api.vidivo.app/v1/payments/invoices/01JN2X8O2R7J1UDVY0AB1BFMJK/download \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."const API = 'https://api.vidivo.app/v1';
// Get payment historyconst { data: transactions, pagination } = await fetch( `${API}/payments/history?start_date=2026-03-01&end_date=2026-03-31`, { headers: { Authorization: `Bearer ${accessToken}` } }).then(r => r.json());
// Export as CSVconst csvBlob = await fetch( `${API}/payments/history/export?format=csv`, { headers: { Authorization: `Bearer ${accessToken}` } }).then(r => r.blob());
// Download invoiceconst { download_url } = await fetch( `${API}/payments/invoices/${invoiceId}/download`, { headers: { Authorization: `Bearer ${accessToken}` } }).then(r => r.json());
window.open(download_url);