# Captions

Live captions in the language being spoken, included with every stream.
These endpoints answer `404` until live captions are enabled for your
organization.

Base URL: `https://api.cardinalstreaming.com`. Authenticate with `Authorization: Bearer <your API key>`.

## Read a customer's caption settings

`GET /v1/customers/{customer_ref}/caption-settings`

The caption settings new streams for this customer start with. `is_default` is true when none have been saved.

### Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `customer_ref` | string | yes | Your identifier for the customer. |

### Examples

cURL:

```bash
curl "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings" \
  -H "Authorization: Bearer $CARDINAL_API_KEY"
```

Python:

```python
import os

import requests

response = requests.get(
    "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings")
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.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings", 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))
}
```

Java:

```java
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/customers/grace-chapel/caption-settings"))
            .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());
    }
}
```

C#:

```csharp
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/customers/grace-chapel/caption-settings");
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()}");
```

### Response 200

The settings.

| Name | Type | Required | Description |
|---|---|---|---|
| `customer_ref` | string | yes | Your identifier for a customer. 1-128 letters, digits, `.`, `_` or `-`; not `.` or `..`. |
| `settings` | object | yes |  |
| `settings.spoken_language` | string | yes | `auto`, or the language spoken: one of `ar de en es fr it ja ko pl pt ru tl uk vi zh`. |
| `settings.translation_languages` | array of string | yes | Reserved for translated captions. Empty. |
| `settings.translation_mode` | string | yes |  One of `"on_demand"`, `"always"`. |
| `settings.profanity_masking` | boolean | yes |  |
| `settings.sung_lyrics` | string | yes | Whether sung lyrics are captioned or shown as a music symbol. One of `"music-symbol"`, `"caption"`. |
| `settings.vocabulary` | array of string | yes | Up to 100 names and terms the captions should spell right. |
| `settings.retired_source_languages` | array of string | no |  |
| `is_default` | boolean | yes | True when nothing has been saved for this customer. |
| `translations_available` | boolean | yes | True when translated captions are priced for the organization, so languages chosen now would be produced. |
| `choices` | object | yes | The languages each field may name, for a form. |
| `choices.spoken` | array of object | yes |  |
| `choices.spoken[].code` | string | yes | The language code, e.g. es or zh-Hans. |
| `choices.spoken[].name` | string | yes | The language's name in itself. |
| `choices.spoken[].beta` | boolean | no | True for a translation still in beta. |
| `choices.translation` | array of object | yes |  |
| `choices.translation[].code` | string | yes | The language code, e.g. es or zh-Hans. |
| `choices.translation[].name` | string | yes | The language's name in itself. |
| `choices.translation[].beta` | boolean | no | True for a translation still in beta. |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `invalid_customer_ref` | The path's customer reference is not valid. |

## Save a customer's caption settings

`PUT /v1/customers/{customer_ref}/caption-settings`

The whole document; a field left out takes its default. New streams for the customer start with these.

### Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `customer_ref` | string | yes | Your identifier for the customer. |

### Request body

| Name | Type | Required | Description |
|---|---|---|---|
| `spoken_language` | string | no |  |
| `translation_languages` | array of string | no |  |
| `translation_mode` | string | no |  One of `"on_demand"`, `"always"`. |
| `profanity_masking` | boolean | no |  |
| `sung_lyrics` | string | no |  One of `"music-symbol"`, `"caption"`. |
| `vocabulary` | array of string | no |  |

### Examples

cURL:

```bash
curl -X PUT "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings" \
  -H "Authorization: Bearer $CARDINAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "spoken_language": "en",
  "profanity_masking": true,
  "vocabulary": [
    "Grace Chapel",
    "Pastor Okafor"
  ]
}'
```

Python:

```python
import os

import requests

response = requests.put(
    "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    json={
        "spoken_language": "en",
        "profanity_masking": True,
        "vocabulary": ["Grace Chapel", "Pastor Okafor"],
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "spoken_language": "en",
    "profanity_masking": true,
    "vocabulary": [
      "Grace Chapel",
      "Pastor Okafor"
    ]
  }),
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "spoken_language": "en",
    "profanity_masking": true,
    "vocabulary": [
      "Grace Chapel",
      "Pastor Okafor"
    ]
  }),
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('PUT', 'https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'json' => [
        'spoken_language' => 'en',
        'profanity_masking' => true,
        'vocabulary' => ['Grace Chapel', 'Pastor Okafor'],
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings")
request = Net::HTTP::Put.new(uri)
request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"
request['Content-Type'] = 'application/json'
request.body = JSON.generate({
  "spoken_language" => "en",
  "profanity_masking" => true,
  "vocabulary" => ["Grace Chapel", "Pastor Okafor"]
})

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.code, response.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	req, err := http.NewRequest("PUT", "https://api.cardinalstreaming.com/v1/customers/grace-chapel/caption-settings", strings.NewReader(`{
  "spoken_language": "en",
  "profanity_masking": true,
  "vocabulary": [
    "Grace Chapel",
    "Pastor Okafor"
  ]
}`))
	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))
}
```

Java:

```java
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/customers/grace-chapel/caption-settings"))
            .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY"))
            .header("Content-Type", "application/json")
            .method("PUT", HttpRequest.BodyPublishers.ofString("{\"spoken_language\":\"en\",\"profanity_masking\":true,\"vocabulary\":[\"Grace Chapel\",\"Pastor Okafor\"]}"))
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

C#:

```csharp
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/customers/grace-chapel/caption-settings");
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));
request.Content = new StringContent("{\"spoken_language\":\"en\",\"profanity_masking\":true,\"vocabulary\":[\"Grace Chapel\",\"Pastor Okafor\"]}", Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
```

### Response 200

The settings as saved.

| Name | Type | Required | Description |
|---|---|---|---|
| `customer_ref` | string | yes | Your identifier for a customer. 1-128 letters, digits, `.`, `_` or `-`; not `.` or `..`. |
| `settings` | object | yes |  |
| `settings.spoken_language` | string | yes | `auto`, or the language spoken: one of `ar de en es fr it ja ko pl pt ru tl uk vi zh`. |
| `settings.translation_languages` | array of string | yes | Reserved for translated captions. Empty. |
| `settings.translation_mode` | string | yes |  One of `"on_demand"`, `"always"`. |
| `settings.profanity_masking` | boolean | yes |  |
| `settings.sung_lyrics` | string | yes | Whether sung lyrics are captioned or shown as a music symbol. One of `"music-symbol"`, `"caption"`. |
| `settings.vocabulary` | array of string | yes | Up to 100 names and terms the captions should spell right. |
| `settings.retired_source_languages` | array of string | no |  |
| `is_default` | boolean | yes | True when nothing has been saved for this customer. |
| `translations_available` | boolean | yes | True when translated captions are priced for the organization, so languages chosen now would be produced. |
| `choices` | object | yes | The languages each field may name, for a form. |
| `choices.spoken` | array of object | yes |  |
| `choices.spoken[].code` | string | yes | The language code, e.g. es or zh-Hans. |
| `choices.spoken[].name` | string | yes | The language's name in itself. |
| `choices.spoken[].beta` | boolean | no | True for a translation still in beta. |
| `choices.translation` | array of object | yes |  |
| `choices.translation[].code` | string | yes | The language code, e.g. es or zh-Hans. |
| `choices.translation[].name` | string | yes | The language's name in itself. |
| `choices.translation[].beta` | boolean | no | True for a translation still in beta. |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `invalid_customer_ref` | The path's customer reference is not valid. |
| 400 | `invalid_body` | The body is not JSON. |
| 400 | `invalid_captions` | A setting is not acceptable; `detail` names the field. |
| 409 | `translations_unavailable` | Translated captions are not available to your organization. |
| 413 | `body_too_large` | The document is over 128 KB. |

## Read a stream's captions status

`GET /v1/streams/{playback_id}/captions`

Whether captions are being produced for a stream right now, and how far behind the video they are.

### Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `playback_id` | string | yes | The stream's `playback_id`. |

### Examples

cURL:

```bash
curl "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions" \
  -H "Authorization: Bearer $CARDINAL_API_KEY"
```

Python:

```python
import os

import requests

response = requests.get(
    "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions")
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.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions", 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))
}
```

Java:

```java
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/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions"))
            .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());
    }
}
```

C#:

```csharp
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/streams/1846bdac2d8c0ce0e846d4a3817012c9/captions");
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()}");
```

### Response 200

The status.

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | string | yes | `delayed` when captions are more than 15 seconds behind the video. One of `"waiting"`, `"running"`, `"delayed"`, `"unavailable"`, `"ended"`. |
| `lag_seconds` | number or null | yes |  |
| `source_language` | string | no |  |
| `translation_mode` | string | no |  |
| `translations_available` | boolean | yes |  |
| `languages` | array of object | yes |  |
| `languages[].lang` | string | yes |  |
| `languages[].name` | string | yes |  |
| `languages[].kind` | string | yes |  One of `"source"`, `"translation"`. |
| `languages[].beta` | boolean | no |  |
| `languages[].producing` | boolean | yes |  |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 404 | `not_found` | No such stream in your organization. |
