Wallet balance and ledger
GET /api/v1/billing/balance returns the prepaid wallet as dollars, whole cents, and micro-cents, alongside the outbound-pause flags billing alerts can set. Poll it before a send to check available funds.
{
"data": {
"balance_usd": 42.5,
"balance_cents": 4250,
"balance_micro_cents": 4250000000,
"credits": 4250,
"currency": "USD",
"outbound_paused": false,
"outbound_block_reason": null,
"payment_failure_count": 0
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
GET /api/v1/billing/transactions pages through the wallet ledger — top-ups, spend debits, refunds, and adjustments — newest first, with next_cursor for paging.
{
"data": {
"transactions": [
{
"id": "txn_01H8XGJ",
"type": "topup",
"amount_minor": 5000,
"reference": "stripe_checkout",
"metadata": null,
"created_at": "2026-08-02T12:00:00.000Z",
"expires_at": null,
"refunded_at": null
},
{
"id": "txn_01H8XGB",
"type": "debit",
"amount_minor": -132,
"reference": "sms_send",
"metadata": null,
"created_at": "2026-08-02T11:47:00.000Z",
"expires_at": null,
"refunded_at": null
}
],
"next_cursor": null,
"has_more": false
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
Top up the wallet
POST /api/v1/billing/balance/top-up returns a hosted Stripe Checkout URL in the locally-hosted checkoutUrl field; the wallet is credited once the payment settles. The Idempotency-Key header dedupes retries, so a repeated request returns the same session instead of charging twice.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/billing/balance/top-up" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
}'
Node.js
import { Orbit } from '@devotel-orbit/node'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://api.orbit.devotel.io/api/v1/billing/balance/top-up', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
}),
})
console.log(await res.json())
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://api.orbit.devotel.io/api/v1/billing/balance/top-up", headers=headers, json={
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
})
print(r.json())
Go
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/billing/balance/top-up", bytes.NewBuffer([]byte(`{
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
Ruby
require 'net/http'
require 'json'
uri = URI('https://api.orbit.devotel.io/api/v1/billing/balance/top-up')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/billing/balance/top-up');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"amount": 5000,
"currency": "usd",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing"
}
JSON);
echo curl_exec($ch);
{
"data": {
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4"
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
Plan checkout and subscription management
POST /api/v1/billing/checkout starts a Stripe Checkout session for a subscription-plan change and returns the hosted checkoutUrl; requests are idempotent for 60 seconds per organization and plan, so a double-click returns the same session URL.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/billing/checkout" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
}'
Node.js
import { Orbit } from '@devotel-orbit/node'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://api.orbit.devotel.io/api/v1/billing/checkout', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
}),
})
console.log(await res.json())
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://api.orbit.devotel.io/api/v1/billing/checkout", headers=headers, json={
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
})
print(r.json())
Go
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/billing/checkout", bytes.NewBuffer([]byte(`{
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
Ruby
require 'net/http'
require 'json'
uri = URI('https://api.orbit.devotel.io/api/v1/billing/checkout')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/billing/checkout');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"planId": "pro",
"successUrl": "https://app.example.com/billing/success",
"cancelUrl": "https://app.example.com/billing/plans"
}
JSON);
echo curl_exec($ch);
{
"data": {
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3"
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
POST /api/v1/billing/portal opens a Stripe customer-portal session so the customer can manage payment methods, subscription, and invoices. Read GET /api/v1/billing/status first and only offer this (or plan checkout above) when can_manage is true.
{
"data": {
"url": "https://billing.stripe.com/p/session/DYb3GT1"
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
Auto top-up configuration
PUT /api/v1/billing/auto-topup charges the saved payment method off-session whenever the balance falls to the threshold. recharge_amount_minor must exceed threshold_minor.
cURL
curl -X PUT "https://api.orbit.devotel.io/api/v1/billing/auto-topup" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
}'
Node.js
import { Orbit } from '@devotel-orbit/node'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://api.orbit.devotel.io/api/v1/billing/auto-topup', {
method: 'PUT',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
}),
})
console.log(await res.json())
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.put("https://api.orbit.devotel.io/api/v1/billing/auto-topup", headers=headers, json={
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
})
print(r.json())
Go
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("PUT", "https://api.orbit.devotel.io/api/v1/billing/auto-topup", bytes.NewBuffer([]byte(`{
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
Ruby
require 'net/http'
require 'json'
uri = URI('https://api.orbit.devotel.io/api/v1/billing/auto-topup')
req = Net::HTTP::Put.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/billing/auto-topup');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "usd"
}
JSON);
echo curl_exec($ch);
{
"data": {
"enabled": true,
"threshold_minor": 500,
"recharge_amount_minor": 5000,
"max_monthly_minor": 50000,
"currency": "USD"
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}
Billing alerts
POST /api/v1/billing/alerts/ creates a spend or balance threshold. At least one email or SMS recipient is required; action_on_hit can be notify, pause_outbound, or block_outbound.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/billing/alerts/" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
}'
Node.js
import { Orbit } from '@devotel-orbit/node'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://api.orbit.devotel.io/api/v1/billing/alerts/', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
}),
})
console.log(await res.json())
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://api.orbit.devotel.io/api/v1/billing/alerts/", headers=headers, json={
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
})
print(r.json())
Go
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/billing/alerts/", bytes.NewBuffer([]byte(`{
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
Ruby
require 'net/http'
require 'json'
uri = URI('https://api.orbit.devotel.io/api/v1/billing/alerts/')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/billing/alerts/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"action_on_hit": "notify",
"cooldown_hours": 24,
"enabled": true
}
JSON);
echo curl_exec($ch);
{
"data": {
"alert": {
"id": "alert_01HZY8ALERT",
"organization_id": "org_01HZY8ORG",
"name": "80% of monthly budget",
"threshold_type": "spend_percent",
"threshold_value": 80,
"currency": "USD",
"notify_emails": [
"billing@example.com"
],
"notify_sms_numbers": "[]",
"action_on_hit": "notify",
"last_triggered_at": null,
"cooldown_hours": 24,
"enabled": true,
"created_by": "user_01HZY8USER",
"created_at": "2026-08-01T09:00:00.000Z",
"updated_at": "2026-08-01T09:00:00.000Z"
}
},
"meta": {
"request_id": "req_01HZY8EXAMPLE",
"timestamp": "2026-08-02T12:00:00.000Z"
}
}