Issue Bluetooth OTA Grant
curl --request POST \
--url https://api.bota.dev/v1/v1/devices/{id}/ota-grant \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"firmware_release_id": "<string>"
}
'import requests
url = "https://api.bota.dev/v1/v1/devices/{id}/ota-grant"
payload = { "firmware_release_id": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({firmware_release_id: '<string>'})
};
fetch('https://api.bota.dev/v1/v1/devices/{id}/ota-grant', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.bota.dev/v1/v1/devices/{id}/ota-grant",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firmware_release_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bota.dev/v1/v1/devices/{id}/ota-grant"
payload := strings.NewReader("{\n \"firmware_release_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.bota.dev/v1/v1/devices/{id}/ota-grant")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"firmware_release_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.bota.dev/v1/v1/devices/{id}/ota-grant")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firmware_release_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyFirmware
Issue Bluetooth OTA Grant
Authorize an App SDK firmware transfer to an enrolled device
POST
/
v1
/
devices
/
{id}
/
ota-grant
Issue Bluetooth OTA Grant
curl --request POST \
--url https://api.bota.dev/v1/v1/devices/{id}/ota-grant \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"firmware_release_id": "<string>"
}
'import requests
url = "https://api.bota.dev/v1/v1/devices/{id}/ota-grant"
payload = { "firmware_release_id": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({firmware_release_id: '<string>'})
};
fetch('https://api.bota.dev/v1/v1/devices/{id}/ota-grant', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.bota.dev/v1/v1/devices/{id}/ota-grant",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'firmware_release_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bota.dev/v1/v1/devices/{id}/ota-grant"
payload := strings.NewReader("{\n \"firmware_release_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.bota.dev/v1/v1/devices/{id}/ota-grant")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"firmware_release_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.bota.dev/v1/v1/devices/{id}/ota-grant")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"firmware_release_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyRequires a secret API key or a restricted key with
Returns
devices:write. Call from
your backend after authorizing the user’s device access; never embed that key
in a mobile app. The device must belong to the key’s project and have an enrolled
public key. The release must be published in the same organization, match the
device model, and contain a Bluetooth image.
string
required
Backend device ID (
dev_*), not the Bluetooth peripheral identifier.string
required
Exact published release (
fw_*) selected using the device-filtered release list.curl -X POST https://api.bota.dev/v1/devices/dev_abc123/ota-grant \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{"firmware_release_id":"fw_abc123"}'
201 with grant_blob (opaque base64) and expires_at (ISO timestamp).
Pass the grant and the same release’s type=ufw download metadata to the App SDK.
Do not log the grant or reuse it after expiry.
Unknown devices, foreign releases, and unpublished releases return 404.
Invalid input, an unenrolled device, a model mismatch, or a missing Bluetooth
image returns 400. Missing authentication returns 401; insufficient scopes
or unsupported token types return 403.
Grant issuance does not schedule a network update or establish successful
installation. Verify the device-reported version after reboot. See
Firmware Updates for both delivery paths.Was this page helpful?

