Poll for the key (returns it exactly once)
curl --request POST \
--url https://api.zinc.com/device/token \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"device_code": "<string>"
}
'import requests
url = "https://api.zinc.com/device/token"
payload = { "device_code": "<string>" }
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({device_code: '<string>'})
};
fetch('https://api.zinc.com/device/token', 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.zinc.com/device/token",
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([
'device_code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"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.zinc.com/device/token"
payload := strings.NewReader("{\n \"device_code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
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.zinc.com/device/token")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"device_code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zinc.com/device/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"device_code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"key": "<string>",
"name": "<string>",
"is_active": true,
"is_test": true,
"created_at": "2023-11-07T05:31:56Z",
"wallet_balance_cents": 123,
"api_key": "<string>",
"sandbox": {
"orders_moved": 123,
"keys_moved": 123,
"note": "Your sandbox order history and its key now belong to this account. The sandbox key keeps working for test mode; use the live key for real orders."
},
"starter_credit": {
"cents": 123,
"reason": "<string>",
"wallet_balance_cents": 123,
"max_price_cents": 123,
"message": "<string>",
"picks": [
{
"title": "<string>",
"url": "<string>",
"retailer": "<string>",
"price_cents": 123
}
]
}
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Device Approval
Redeem a Device Code
Poll with your device code until the owner approves, then receive the live API key exactly once.
POST
/
device
/
token
Poll for the key (returns it exactly once)
curl --request POST \
--url https://api.zinc.com/device/token \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"device_code": "<string>"
}
'import requests
url = "https://api.zinc.com/device/token"
payload = { "device_code": "<string>" }
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({device_code: '<string>'})
};
fetch('https://api.zinc.com/device/token', 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.zinc.com/device/token",
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([
'device_code' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"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.zinc.com/device/token"
payload := strings.NewReader("{\n \"device_code\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
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.zinc.com/device/token")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"device_code\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zinc.com/device/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"device_code\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"key": "<string>",
"name": "<string>",
"is_active": true,
"is_test": true,
"created_at": "2023-11-07T05:31:56Z",
"wallet_balance_cents": 123,
"api_key": "<string>",
"sandbox": {
"orders_moved": 123,
"keys_moved": 123,
"note": "Your sandbox order history and its key now belong to this account. The sandbox key keeps working for test mode; use the live key for real orders."
},
"starter_credit": {
"cents": 123,
"reason": "<string>",
"wallet_balance_cents": 123,
"max_price_cents": 123,
"message": "<string>",
"picks": [
{
"title": "<string>",
"url": "<string>",
"retailer": "<string>",
"price_cents": 123
}
]
}
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Poll this with the
device_code from POST /device/code every interval seconds. On approval it returns the live key — once. Persist it immediately.
curl -X POST https://api.zinc.com/device/token \
-H "Content-Type: application/json" \
-d '{"device_code": "DE_..."}'
Polling states
| Status | error.code | What to do |
|---|---|---|
400 | authorization_pending | The human has not decided yet. Poll again after interval. |
400 | slow_down | You are polling too fast. Back off, then continue. |
400 | expired_token | The code aged out. Start over with a new one. |
403 | access_denied | The owner declined. Stop; do not mint another code. |
200 | — | Approved. Read api_key and stop polling. |
What arrives with the key
wallet_balance_cents— spendable balance of the account the key belongs to.sandbox— what the sandbox claim moved, when azn_test_key was attached.starter_credit— credit granted alongside the key, if the account earned one, with a few orderable picks it covers.
key and api_key are returned on this response only. Nothing stores the plaintext afterwards, so write it to your credential store before doing anything else.Authorizations
Zinc API key (Bearer zn_...)
Body
application/json
Required string length:
16 - 128Response
Successful Response
The issued key, plus what the agent needs to act on it right away.
Spendable wallet balance of the account the key belongs to.
What the sandbox claim moved, when sandbox_key was sent.
Show child attributes
Show child attributes
Wallet credit granted alongside this key, if the account earned one, with a few items it is known to cover.
Show child attributes
Show child attributes

