API Reference

The Attendi Speech Service API allows you to quickly obtain an accurate transcription of an uploaded audio file. The API is a SaaS solution for healthcare institutions. We have different language models dedicated to a variety of domains within healthcare. This document will take you through the basic steps to get the Attendi Speech Service API up and running, so that you can integrate it seamlessly into your own application. We will show you how to authenticate your requests to the API, link customers and transcribe audio.

API Keys

The Attendi Speech Service API uses your account’s API keys to authenticate requests. Attendi will provide you with your API keys. If you don’t include your API key when making an API request, the Speech Service API returns an error.

Customers

You can create customers that will be part of your account. Each customer will get an API key, so that you can authenticate your request, and identify your customer to us at the same time.

There are two types of API keys: customer and secret.

  • Customer keys are used to identify your tenant and the specific customer the transcribe request is for. It is not a secret. This means that you can use the publishable key in your JavaScript frontend, or in a mobile application.
  • Secret keys should be kept confidential, and you should only store them on your servers. You must not share your secret API key with any third parties. The secret API key should be treated like a password.
Key Prefix Authorization
Customer ck_ Idenitifies your customer and allows you to transcribe audio directly from a client application where the API key cannot be secured
Secret sk_ Use the Customers endpoints. Transcribe audio via machine-to-machine communication

Authentication

Authentication to the API is done via a custom HTTP header. Provide your API key in the following header: x-API-key.

All API requests must be made over HTTPS. Requests made over HTTP will fail, so will API requests made without authentication.

Cross-Origin requests are only allowed for the transcribe endpoint, using your customer key from a frontend application.

Security

We implemented and rely on end-to-end TLS/SSL encryption. This allows us to encrypt and securely transmit data to the backend and send the transcribed result to your app. We only accept requests over HTTPS.

Quickstart guide 

Customers

There are several customer endpoints that allow you to create, update or retrieve customers. You need to create a customer with the id you use in your internal systems. There is a PUT method that will allow you to upsert a customer. This way, it easy to syncronize changes from your internal systems to ours, just send the complete object everytime. We also have a PATCH endpoint if you only want to update a specific field. You can retrieve a specific customer by your ID, or get all customers.

Customer API Key

Each customer will have it’s own API key. This key starts with **ck_**. You can reroll the API key by using the reroll endpoint.

Model Type

When creating or updating a customer, you need to provide a default model value for the customer. This model determines what Speech to Text model we will use if you do not provide an override value in the transcribe request. This model type can be updated with the PUT and PATCH endpoints.

Linking customers

NOTE: Only for secret API keys. This behaviour is included automatically with customer keys.

Requests to transcribe audio with the Attendi Speech Service API can be parameterized with a customer ID, allowing you to keep track of the usage of each specific customer.

Units

Units are the representation in our system for a location or a team of a customer. Sometimes, customers want to roll out per location. In order to make it easy to measure the adoption rate and activity per location/team, we need to know the teams and locations.

The endpoints for units are very similar to customer. As a unit is created within a customer, the URI always starts with /v1/customers/{customerId}/units. Then you can use PUT to upsert a unit again, using your own ID to create the unit in the Attendi system. There is also a PATCH endpoint to update a existing unit, using your ID. For each customer, you can retrieve a specific unit by ID, or retrieve all units for a specific customer.

Transcribing audio

You can send a POST request to the /v1/speech/transcribe endpoint to obtain a transcription of an audio file. The audio file you want to transcribe must be encoded in base64 so that the binary audio data is converted into characters. Additionally, the audio should meet the following specifications:

  • Single (mono) channel recording
  • 16 kHz sampling rate
  • 16-bit audio recording

In case your file does not meet these requirements, you can use a tool to convert it. ffmpeg is a command line tool, which you can use to convert your audio as follows:

ffmpeg -i {input_file.wav} -acodec pcm_s16le -ac 1 -ar 16000 {output_file.wav}

Besides the audio file, it is required to select the specific language model you want to use (DistrictCare, ResidentialCare or MentalHealthCare). You also have to add a unique UserID. The unique UserID could be anything, for example a UUID. We do not save any personal data, only the unique UserID. We use this UserID to measure usage, and to ensure that we can easily trace back any problems with the functionality. Furthermore, there is an option to add the browser’s User-Agent as metadata.

Environments

Environment Use URL
SANDBOX Test the API on a test enviroment https://sandbox.api.attendi.nl
PRODUCTION Run the API on a production environment https://api.attendi.nl

Example code

API docs

Transcribe

1
curl --request POST 'https://sandbox.api.attendi.nl/v1/speech/transcribe' --header 'Content-Type: application/json' --header 'x-API-key: ck_<key_here>' --data-raw '{"userId": "<user_id_here>", "unitId": "<unit_id_here>", "config": {"model": "<add_model_here>"}, "metadata": {"userAgent": "string"}, "audio": "base64_encoded_audio_here"}' 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
    byte[] audioData = { } // audio bytes
    var config = new TranscribeConfig("ResidentialCare");
    var metadata = new TranscribeMetadata(".NET Console App");
    var request = new TranscribeRequest(audioData, "<user_id_here>", "<unit_id_here>", config, metadata);
    var client = new HttpClient();
    client.DefaultRequestHeaders.Add("x-API-key", "ck_<key_here>");
    var response = await client.PostAsJsonAsync("https://sandbox.api.attendi.nl/v1/speech/transcribe", request);
    var body = await response.Content.ReadAsStringAsync();
    Console.WriteLine(body);
}

public class TranscribeRequest
{
    public TranscribeRequest(byte[] audio, string userId, string unitId, TranscribeConfig transcribeConfig, TranscribeMetadata metadata = null)
    {
        Audio = audio;
        UserId = userId;
        UnitId = unitId;
        Config = transcribeConfig;
        Metadata = metadata;
    }

    [Required]
    public byte[] Audio { get; private set; }
    [Required]
    public string UserId { get; private set; }
    public string UnitId {get; private set;}
    public TranscribeConfig Config { get; private set; }
    public TranscribeMetadata Metadata { get; private set; }
}

public class TranscribeConfig
{
    [Required]
    public string Model { get; private set; }

    public TranscribeConfig(string model)
    {
        Model = model;
    }
}

public class TranscribeMetadata
{
    public string UserAgent { get; private set; }

    public TranscribeMetadata(string userAgent)
    {
        UserAgent = userAgent;
    }
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import base64
import requests

audio_file = # Path to audio file to transcribe

with open(audio_file, "rb") as f:
  audio = f.read()

base64_audio = base64.b64encode(audio).decode("utf-8")

response = requests.post(
  "https://sandbox.api.attendi.nl/v1/speech/transcribe",
  headers={"x-api-key": 'ck_<key_here>'},
  json={"userId": "<user_id_here>", "unitId": "<unit_id_here>", "config": {"model": "<add_model_here"}, "metadata": {"userAgent": "string"}, "audio": base64_audio})

print(response.json())

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// SpeechAPI.ts
const convertBlobToBase64 = (blob: Blob) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = reject;
    reader.onload = () => {
      const base64String = reader.result?.toString();
      if (base64String) {
        resolve(base64String.substr(base64String.indexOf(",") + 1));
      } else {
        resolve(reader.result);
      }
    };
    reader.readAsDataURL(blob);
  });

export async function transcribeAudio(
  blob: Blob,
  userId: string,
  unitId: string,
  modelType: string
) {
  const blobBase64 = await convertBlobToBase64(blob);

  const body = {
    audio: blobBase64,
    userId: userId,
    unitId: unitId,
    config: {
      model: modelType,
    },
    metadata: {
      userAgent: navigator.userAgent, // only works in browser
    },
  };

  return API.post<ITranscript>("/v1/speech/transcribe", body);
}
// ITranscript.ts
export interface ITranscript {
  transcript: string;
}

// APIUtils.ts
import axios from "axios";

export const API = axios.create({
  baseURL: process.env.REACT_APP_BASE_API_URL,
  headers: {
    "Content-Type": "application/json",
    "x-API-key": process.env.REACT_APP_API_KEY,
  },
});

Customers

UPSERT CUSTOMER

1
curl --request PUT 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>' --header 'Content-Type: application/json' --header 'x-API-key: sk_<key_here>' --data-raw '{ "name": "MyTestCustomer", "defaultModel": "<model_type_here>" }'

RETRIEVE CUSTOMER

1
curl --request GET 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>' --header 'x-API-key: sk_<key_here>'

LIST CUSTOMERS

1
curl --request GET 'https://sandbox.api.attendi.nl/v1/customers' --header 'x-API-key: sk_<key_here>'

UPDATE CUSTOMER

1
curl --request PATCH 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>' --header 'Content-Type: application/merge-patch+json' --header 'x-API-key: sk_<key_here>' --data-raw '{ "name": "MyUpdatedCustomer" }'

ROLL API KEY CUSTOMER

1
curl --request POST 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/api_keys/roll' --header 'x-API-key: sk_<key_here>'

UPSERT CUSTOMER

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public async Task UpsertCustomer()
{
    var request = new UpsertCustomerRequest("MyCustomer", "<model_type_here>");

    string jsonString = JsonSerializer.Serialize(request);
    var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");

    var response = await _client.PutAsync($"https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>", httpContent);
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

public class UpsertCustomerRequest
{
    public UpsertCustomerRequest(string name, string defaultModel)
    {
        Name = name;
        DefaultModel = defaultModel;
    }

    public string Name { get; private set; }
    public string DefaultModel { get; private set; }
}

RETRIEVE CUSTOMER

1
2
3
4
5
6
7
public async Task GetCustomers()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.GetAsync("https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>");
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

LIST CUSTOMERS

1
2
3
4
5
6
7
public async Task GetCustomers()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.GetAsync("https://sandbox.api.attendi.nl/v1/customers");
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

UPDATE CUSTOMER

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public async Task UpdateCustomer()
{
    var request = new UpdateCustomerRequest("MyUpdatedCustomer", null);

    var options = new JsonSerializerOptions
    {
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };

    string jsonString = JsonSerializer.Serialize(request, options);
    // Note the mediaType, it's for merge patches
    var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/merge-patch+json");

    var response = await _client.PatchAsync($"https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>", httpContent);
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

public class UpdateCustomerRequest
{
    public UpdateCustomerRequest(string name, string defaultModel)
    {
        Name = name;
        DefaultModel = defaultModel;
    }

    public string Name { get; private set; }
    public string DefaultModel { get; private set; }
}

ROLL API KEY CUSTOMER

1
2
3
4
5
6
7
public async Task GetCustomers()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.PostAsync("https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/api_keys/roll", null);
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

UPSERT CUSTOMER

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>"

payload = {'name': 'MyCustomer', 'defaultModel': '<model_type_here>'}
headers = {'Content-Type': 'application/json', 'x-API-key': 'sk_<key_here>'}
response = requests.request("PUT", url, headers=headers, json=payload)

print(response) # Will print <Response [201] or [200] if successful
print(response.text)

RETRIEVE CUSTOMER

1
2
3
4
5
6
7
8
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>"

headers = {'x-API-key': 'sk_<key_here>'}

response = requests.request("GET", url, headers=headers)
print(response.text)

LIST CUSTOMERS

1
2
3
4
5
6
7
8
import requests

url = "https://sandbox.api.attendi.nl/v1/customers"

headers = {'x-API-key': 'sk_<key_here>'}

response = requests.request("GET", url, headers=headers)
print(response.text)

UPDATE CUSTOMER

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>"

payload = {'name': 'MyUpdatedCustomer'}
headers = {'Content-Type': 'application/merge-patch+json', 'x-API-key': 'sk_<key_here>'}
response = requests.request("PATCH", url, headers=headers, json=payload)

print(response) # Will print <Response [200] if successful
print(response.text)

ROLL API KEY CUSTOMER

1
2
3
4
5
6
7
8
9
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/api_keys/roll"

headers = {'Content-Type': 'application/json', 'x-API-key': 'sk_<key_here>'}
response = requests.request("POST", url, headers=headers)

print(response) # Will print <Response [200] if successful
print(response.text)

Units

UPSERT UNIT

1
curl --request PUT 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>' --header 'Content-Type: application/json' --header 'x-API-key: sk_<key_here>' --data-raw '{ "name": "MyTestUnit" }'

RETRIEVE UNIT

1
curl --request GET 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>' --header 'x-API-key: sk_<key_here>'

LIST UNITS

1
curl --request GET 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units' --header 'x-API-key: sk_<key_here>'

UPDATE UNIT

1
curl --request PATCH 'https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>' --header 'Content-Type: application/merge-patch+json' --header 'x-API-key: sk_<key_here>' --data-raw '{ "name": "MyUpdatedTestUnit" }'

UPSERT UNIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public async Task UpsertUnit()
{
    var request = new UpsertUnitRequest("MyUnit");

    string jsonString = JsonSerializer.Serialize(request);
    var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");

    var response = await _client.PutAsync($"https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>", httpContent);
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

public class UpsertUnitRequest
{
    public UpsertUnitRequest(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}

RETRIEVE UNIT

1
2
3
4
5
6
7
public async Task GetCustomers()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.GetAsync("https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>");
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

LIST UNITS

1
2
3
4
5
6
7
public async Task GetCustomers()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.GetAsync("https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units");
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

UPDATE UNIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public async Task UpdateCustomer()
{
    var request = new UpdateUnitRequest("MyUpdatedUnit");

    string jsonString = JsonSerializer.Serialize(request);
    // Note the mediaType, it's for merge patches
    var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/merge-patch+json");

    var response = await _client.PatchAsync($"https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>", httpContent);
    var responseBody = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseBody);
}

public class UpdateUnitRequest
{
    public UpdateUnitRequest(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}

UPSERT UNIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>"

payload = {'name': 'MyUnit'}
headers = {'Content-Type': 'application/json', 'x-API-key': 'sk_<key_here>'}
response = requests.request("PUT", url, headers=headers, json=payload)

print(response) # Will print <Response [201] or [200] if successful
print(response.text)

RETRIEVE UNIT

1
2
3
4
5
6
7
8
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>"

headers = {'x-API-key': 'sk_<key_here>'}

response = requests.request("GET", url, headers=headers)
print(response.text)

LIST UNITS

1
2
3
4
5
6
7
8
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units"

headers = {'x-API-key': 'sk_<key_here>'}

response = requests.request("GET", url, headers=headers)
print(response.text)

UPDATE UNIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import requests

url = "https://sandbox.api.attendi.nl/v1/customers/<customer_id_here>/units/<unit_id_here>"

payload = {'name': 'MyUpdatedUnit'}
headers = {'Content-Type': 'application/merge-patch+json', 'x-API-key': 'sk_<key_here>'}
response = requests.request("PATCH", url, headers=headers, json=payload)

print(response) # Will print <Response [200] if successful
print(response.text)

DON'T JUST TAKE OUR WORD FOR IT

Are you interested in the possibilities with Attendi? Please contact us for a demo or a conversation.