Device Flow
Authenticate users from CLI tools and devices without a browser
The Device Flow (RFC 8628) lets users authorize your app from a separate device with a browser. This is ideal for CLI tools, IoT devices, and environments without direct browser access.
Flow Overview#
- Your app requests a device code from Orshot
- Orshot returns a
user_codeand a verification URL - You display the code and URL to the user
- The user visits the URL in a browser, enters the code, and approves access
- Meanwhile, your app polls the token endpoint until the user completes authorization
Step 1: Request a Device Code#
curl -X POST https://api.orshot.com/v1/oauth/device/code \
-H "Content-Type: application/json" \
-d '{
"client_id": "YOUR_CLIENT_ID",
"scope": "workspace:read render:generate"
}'Response:
{
"device_code": "device-code-string",
"user_code": "A1B2-C3D4",
"verification_uri": "https://orshot.com/oauth/device",
"verification_uri_complete": "https://orshot.com/oauth/device?user_code=A1B2-C3D4",
"expires_in": 600,
"interval": 5
}| Field | Description |
|---|---|
device_code | Used by your app to poll for the token (keep this secret) |
user_code | Displayed to the user — they enter this on the verification page |
verification_uri | The URL the user visits to enter the code |
verification_uri_complete | URL with the code pre-filled (for QR codes, clickable links) |
expires_in | How long the codes are valid (600 seconds = 10 minutes) |
interval | Minimum seconds between polling requests |
Step 2: Display the Code#
Show the user the code and where to go:
To authorize this app, visit:
https://orshot.com/oauth/device
And enter the code:
A1B2-C3D4Or provide the verification_uri_complete as a clickable link or QR code.
Step 3: Poll for Tokens#
While the user is authorizing, poll the token endpoint at the specified interval:
curl -X POST https://api.orshot.com/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"client_id": "YOUR_CLIENT_ID",
"device_code": "device-code-string"
}'While waiting (user hasn't approved yet):
{
"error": "authorization_pending"
}After the user approves:
{
"access_token": "ost_...",
"refresh_token": "osr_...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "workspace:read render:generate",
"user_id": "user-uuid",
"workspace_ids": ["workspace-uuid-1"]
}Polling Errors#
| Error | Meaning | Action |
|---|---|---|
authorization_pending | User hasn't approved yet | Keep polling at the specified interval |
slow_down | You're polling too fast | Increase your polling interval by 5 seconds |
expired_token | The device code has expired | Start over from Step 1 |
access_denied | User denied the request | Show an error to the user |
Full Example (Node.js CLI)#
const CLIENT_ID = process.env.ORSHOT_CLIENT_ID;
async function authenticate() {
// Step 1: Get device code
const codeRes = await fetch("https://api.orshot.com/v1/oauth/device/code", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: CLIENT_ID,
scope: "workspace:read render:generate",
}),
});
const { device_code, user_code, verification_uri, interval } =
await codeRes.json();
// Step 2: Show the user
console.log(`\nVisit: ${verification_uri}`);
console.log(`Enter code: ${user_code}\n`);
console.log("Waiting for authorization...");
// Step 3: Poll for tokens
let pollInterval = interval * 1000;
while (true) {
await new Promise((r) => setTimeout(r, pollInterval));
const tokenRes = await fetch("https://api.orshot.com/v1/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
client_id: CLIENT_ID,
device_code,
}),
});
const data = await tokenRes.json();
if (data.access_token) {
console.log(
"Authorized! Access granted to workspaces:",
data.workspace_ids,
);
return data;
}
if (data.error === "slow_down") {
pollInterval += 5000;
} else if (data.error !== "authorization_pending") {
throw new Error(`Authorization failed: ${data.error}`);
}
}
}User Experience Tips#
- Always show
verification_uri_completewhen possible — saves users from typing the code - Display a QR code linking to
verification_uri_completefor mobile-friendly approval - Show a clear "Waiting for authorization..." message while polling
- Handle
expired_tokengracefully — offer to restart the flow

All Set? Let's Start Automating
- Image, PDF and Video Generation via API
- Canva like editor with AI and smart features
- No-Code Integrations (Zapier, Make, n8n etc.)
- Embed Orshot Studio in your app
- Start Free. No credit card required. Cancel anytime.