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, create customers and units, and transcribe audio.

API Keys

The Attendi Speech Service API uses 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 a 401 Unauthorized error.

Customers

You can create customers that will be part of your tenant. Each customer will get their own 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. They are 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 should only be stored 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 & Units endpoints

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 can 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 is easy to syncronize changes from your internal systems to ours, make sure to send the complete request object every time. 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 retrieve all customers.

Customer API Key

Each customer has it’s own API key. This key starts with ck_**. You can roll the customer key using the roll endpoint, which expects an amount of seconds the key should expire in the request.

Model Type

When creating a customer, you need to provide a default model 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. The default model can be updated with the PUT and PATCH endpoints. Currently we support the following models: DistrictCare, ResidentialCare and MentalHealthCare.

Units

Units are the representation in our system for a customer’s location or a team. Sometimes, customers want to roll out per location. In order to measure the adoption rate and activity per location/team, we need to know the units.

The endpoints for units are similar to the customer endpoints. As a unit is created within a customer, the URI always starts with /v1/customers/{customerId}/units. You can use PUT to upsert a unit using your own ID to create the unit in the Attendi system. Please make sure to send the entire request object every time. There is also a PATCH endpoint to update an existing unit, using your ID. For each customer, you can retrieve a specific unit by ID, or retrieve all units for a specific customer. When creating a unit, you need to provide the total amount of potential users for the unit. This number helps the customer with the roll-out of the functionality.

Transcribing audio

You can send a POST request to the /v1/speech/transcribe endpoint to obtain a transcription of an audio file using your customer key. 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. For example, 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 provide 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 API. There is also an option to add the browser’s User-Agent as metadata. We also advise you to send the user’s UnitID in the request, so that usage can be measured.

The output of the API is a JSON object.

{

 "transcript": "TEXT"

}

You need to extract the “TEXT” from “transcript” and place it in the correct text field.

Environments

We provide two URLs for you to send your requests to. The sandbox URL should be used for test environments, while the production URL should be used strictly for production 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/api_keys/<id>/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 GetCustomer()
{
    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 RollAPiKeyCustomer()
{
    client.DefaultRequestHeaders.Add("x-API-key", "sk_<key_here>");
    var response = await _client.PostAsync("https://sandbox.api.attendi.nl/v1/api_keys/{id}/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/api_keys/<id>/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", "users": <number_potential_users> }'

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", "users": <number_potential_users> }'

UPSERT UNIT

 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 UpsertUnit()
{
    var request = new UpsertUnitRequest("MyUnit", <number_potential_users>);

    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, int users)
    {
        Name = name;
		Users = users;
    }

    public string Name { get; private set; }
	public int Users { get; private set; }
}

RETRIEVE UNIT

1
2
3
4
5
6
7
public async Task GetUnit()
{
    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 GetUnits()
{
    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
23
24
public async Task UpdateUnit()
{
    var request = new UpdateUnitRequest("MyUpdatedUnit", <number_potential_users>);

    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, int users)
    {
        Name = name;
		Users = users;
    }

    public string Name { get; private set; }
	public int Users { 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', "users": <number_potential_users>}
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', "users": <number_potential_users>}
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.