Billing API
Your invoices, the card on file, and paying an invoice now. These work even while streaming is paused for an unpaid bill, so the bill can always be paid.
This page is generated from the OpenAPI specification; the same text is inMarkdown for tools and AI assistants.
Read your billing
GET/v1/billing
Your last invoices, the card on file and your account credit. Works while streaming is paused for an unpaid bill.
Response 200
Billing.
Fields
| Name | Type | Description |
|---|---|---|
| billed_by_cardrequired | boolean | False when your organization is invoiced by agreement. |
| invoicesrequired | array of object | |
| invoices[].idrequired | string (uuid) | |
| invoices[].periodrequired | string |
|
| invoices[].statusrequired | string | One of |
| invoices[].usage_centsrequired | integer | |
| invoices[].platform_fee_centsrequired | integer | |
| invoices[].carried_in_centsrequired | integer | |
| invoices[].total_centsrequired | integer | |
| invoices[].credit_centsrequired | integer | |
| invoices[].charged_centsrequired | integer | |
| invoices[].refunded_centsrequired | integer | |
| invoices[].line_itemsrequired | array of object | |
| invoices[].line_items[].productrequired | string | One of |
| invoices[].line_items[].tierrequired | string | |
| invoices[].line_items[].minutesrequired | integer | |
| invoices[].line_items[].centsrequired | integer | |
| invoices[].attemptsrequired | integer | |
| invoices[].next_attempt_atrequired | string (date-time) or null | |
| invoices[].last_error_classrequired | string or null | Why the last charge failed. Treat a value not listed here as One of |
| invoices[].last_error_messagerequired | string or null | The same reason in plain words, with what to do about it, fit to show a person. |
| invoices[].processor_transaction_idrequired | string or null | |
| invoices[].paid_atrequired | string (date-time) or null | |
| invoices[].created_atrequired | string (date-time) | |
| credit_balance_centsrequired | integer | |
| streaming_pausedrequired | boolean | A bill is unpaid after its seven days: no stream can be booked or started until it is paid. |
| card | object | |
| card.brandrequired | string or null | |
| card.last4required | string or null | |
| card.exp_monthrequired | integer or null | |
| card.exp_yearrequired | integer or null | |
| card.has_backuprequired | boolean | |
| billing_email | string or null | |
| platform_fee_cents | integer | |
| processor_customer_id | string or null | Your customer record at the payment processor, set by your first card.
Tokenize any later card onto this record (pass it as |
Errors
| Status | Code | Meaning |
|---|---|---|
| 503 | billing_unavailable | Billing is not available on this deployment. |
curl "https://api.cardinalstreaming.com/v1/billing" \ -H "Authorization: Bearer $CARDINAL_API_KEY"import os
import requests
response = requests.get( "https://api.cardinalstreaming.com/v1/billing", headers={ "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}", }, timeout=30,)print(response.status_code, response.text)const response = await fetch('https://api.cardinalstreaming.com/v1/billing', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data = await response.json();console.log(response.status, data);const response: Response = await fetch('https://api.cardinalstreaming.com/v1/billing', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data: unknown = await response.json();console.log(response.status, data);<?phprequire 'vendor/autoload.php'; // composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/billing', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'), ], 'http_errors' => false,]);echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;require 'json'require 'net/http'
uri = URI("https://api.cardinalstreaming.com/v1/billing")request = Net::HTTP::Get.new(uri)request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }puts response.code, response.bodypackage main
import ( "fmt" "io" "net/http" "os")
func main() { req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/billing", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))
resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(data))}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;
public class Example { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.cardinalstreaming.com/v1/billing")) .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY")) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); }}using System.Net.Http.Headers;using System.Text;
using var client = new HttpClient();using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/billing");request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));
using var response = await client.SendAsync(request);Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");Read the bill so far
GET/v1/billing/upcoming
This month’s invoice as it stands: usage so far priced as the invoice will price it, the platform fee, anything carried, the credit drawn first, the estimated charge and its date, and what is still owed on earlier invoices.
Response 200
The bill so far.
Fields
| Name | Type | Description |
|---|---|---|
| periodrequired | string | The current month, |
| billed_by_cardrequired | boolean | |
| linesrequired | array of object | |
| lines[].productrequired | string | One of |
| lines[].tierrequired | string | One of |
| lines[].minutesrequired | integer | Billed minutes. |
| lines[].centsrequired | integer | The amount in whole cents, exactly as invoiced. |
| usage_centsrequired | integer | |
| platform_fee_centsrequired | integer | |
| carried_centsrequired | integer | |
| credits_applied_centsrequired | integer | |
| estimated_total_centsrequired | integer | |
| charge_onrequired | string (date) | |
| outstanding_centsrequired | integer | |
| outstanding_invoicesrequired | integer | |
| card_last4required | string or null | |
| streaming_pausedrequired | boolean | A bill is unpaid after its seven days: no stream can be booked or started until it is paid. |
Errors
| Status | Code | Meaning |
|---|---|---|
| 503 | billing_unavailable | Billing is not available on this deployment. |
curl "https://api.cardinalstreaming.com/v1/billing/upcoming" \ -H "Authorization: Bearer $CARDINAL_API_KEY"import os
import requests
response = requests.get( "https://api.cardinalstreaming.com/v1/billing/upcoming", headers={ "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}", }, timeout=30,)print(response.status_code, response.text)const response = await fetch('https://api.cardinalstreaming.com/v1/billing/upcoming', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data = await response.json();console.log(response.status, data);const response: Response = await fetch('https://api.cardinalstreaming.com/v1/billing/upcoming', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data: unknown = await response.json();console.log(response.status, data);<?phprequire 'vendor/autoload.php'; // composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/billing/upcoming', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'), ], 'http_errors' => false,]);echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;require 'json'require 'net/http'
uri = URI("https://api.cardinalstreaming.com/v1/billing/upcoming")request = Net::HTTP::Get.new(uri)request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }puts response.code, response.bodypackage main
import ( "fmt" "io" "net/http" "os")
func main() { req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/billing/upcoming", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))
resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(data))}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;
public class Example { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.cardinalstreaming.com/v1/billing/upcoming")) .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY")) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); }}using System.Net.Http.Headers;using System.Text;
using var client = new HttpClient();using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/billing/upcoming");request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));
using var response = await client.SendAsync(request);Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");Put a card on file
PUT/v1/billing/card
Sets the card streaming is billed to (or the backup card, tried when the
main one is declined). A card number never reaches this API: the console
collects it in the browser straight to the payment processor, which
returns the payment_method_id you send here.
Request body
| Name | Type | Description |
|---|---|---|
| payment_method_idrequired | string | |
| backup | boolean | Set the backup card instead of the main one. |
Response 204
The card is on file.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_body | The body is not JSON. |
| 400 | invalid_request | The payment method is missing, unknown, removed, not a card, or another customer’s; or it is the organization’s first card and was not stored in the last hour. |
| 503 | billing_unavailable | Billing is not available on this deployment. |
curl -X PUT "https://api.cardinalstreaming.com/v1/billing/card" \ -H "Authorization: Bearer $CARDINAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "payment_method_id": "pm_8f3c2a91d0", "backup": false}'import os
import requests
response = requests.put( "https://api.cardinalstreaming.com/v1/billing/card", headers={ "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}", }, json={ "payment_method_id": "pm_8f3c2a91d0", "backup": False, }, timeout=30,)print(response.status_code, response.text)const response = await fetch('https://api.cardinalstreaming.com/v1/billing/card', { method: 'PUT', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "payment_method_id": "pm_8f3c2a91d0", "backup": false }),});const data = await response.json();console.log(response.status, data);const response: Response = await fetch('https://api.cardinalstreaming.com/v1/billing/card', { method: 'PUT', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "payment_method_id": "pm_8f3c2a91d0", "backup": false }),});const data: unknown = await response.json();console.log(response.status, data);<?phprequire 'vendor/autoload.php'; // composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();$response = $client->request('PUT', 'https://api.cardinalstreaming.com/v1/billing/card', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'), ], 'json' => [ 'payment_method_id' => 'pm_8f3c2a91d0', 'backup' => false, ], 'http_errors' => false,]);echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;require 'json'require 'net/http'
uri = URI("https://api.cardinalstreaming.com/v1/billing/card")request = Net::HTTP::Put.new(uri)request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"request['Content-Type'] = 'application/json'request.body = JSON.generate({ "payment_method_id" => "pm_8f3c2a91d0", "backup" => false})
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }puts response.code, response.bodypackage main
import ( "fmt" "io" "net/http" "os" "strings")
func main() { req, err := http.NewRequest("PUT", "https://api.cardinalstreaming.com/v1/billing/card", strings.NewReader(`{ "payment_method_id": "pm_8f3c2a91d0", "backup": false}`)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY")) req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(data))}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;
public class Example { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.cardinalstreaming.com/v1/billing/card")) .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY")) .header("Content-Type", "application/json") .method("PUT", HttpRequest.BodyPublishers.ofString("{\"payment_method_id\":\"pm_8f3c2a91d0\",\"backup\":false}")) .build(); HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); }}using System.Net.Http.Headers;using System.Text;
using var client = new HttpClient();using var request = new HttpRequestMessage(new HttpMethod("PUT"), "https://api.cardinalstreaming.com/v1/billing/card");request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));request.Content = new StringContent("{\"payment_method_id\":\"pm_8f3c2a91d0\",\"backup\":false}", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");Pay an invoice now
POST/v1/billing/invoices/{invoice_id}/pay
Charges an unpaid invoice now, after any account credit. Paying the
bill that paused streaming resumes it at once.
A declined card is 402 payment_failed and changes nothing else.
Path parameters
| Name | Type | Description |
|---|---|---|
| invoice_idrequired | string (uuid) | The invoice’s |
Response 200
Paid, or settled by credit, or carried into next month because what remained was under $0.50.
Fields
| Name | Type | Description |
|---|---|---|
| statusrequired | string | One of |
| error_class | string | |
| message | string | |
| suspendedrequired | boolean | Whether this attempt paused streaming. A payment never does. |
| charged_centsrequired | integer | |
| credited_centsrequired | integer |
Errors
| Status | Code | Meaning |
|---|---|---|
| 402 | payment_failed | The card was not charged; |
| 404 | not_found | No such invoice in your organization. |
| 409 | not_payable | The invoice is not owed. |
| 503 | billing_unavailable | Billing is not available on this deployment. |
curl -X POST "https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay" \ -H "Authorization: Bearer $CARDINAL_API_KEY"import os
import requests
response = requests.post( "https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay", headers={ "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}", }, timeout=30,)print(response.status_code, response.text)const response = await fetch('https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay', { method: 'POST', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data = await response.json();console.log(response.status, data);const response: Response = await fetch('https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay', { method: 'POST', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data: unknown = await response.json();console.log(response.status, data);<?phprequire 'vendor/autoload.php'; // composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();$response = $client->request('POST', 'https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'), ], 'http_errors' => false,]);echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;require 'json'require 'net/http'
uri = URI("https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay")request = Net::HTTP::Post.new(uri)request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }puts response.code, response.bodypackage main
import ( "fmt" "io" "net/http" "os")
func main() { req, err := http.NewRequest("POST", "https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))
resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(data))}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;
public class Example { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay")) .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY")) .method("POST", HttpRequest.BodyPublishers.noBody()) .build(); HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); }}using System.Net.Http.Headers;using System.Text;
using var client = new HttpClient();using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api.cardinalstreaming.com/v1/billing/invoices/0d6f2b1c-3e4a-4b8f-9c11-5a7d2e9f8b10/pay");request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));
using var response = await client.SendAsync(request);Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");{ "status": "succeeded", "charged_cents": 2750, "credited_cents": 1500, "suspended": false}