Skip to content

Commit 4d4be5c

Browse files
committed
Hookup /auth/key-request/exchange. Small docgen fix
1 parent 2b5a17d commit 4d4be5c

27 files changed

Lines changed: 4001 additions & 896 deletions

docs/md/api/auth.md

Lines changed: 174 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import ApiTester from '@site/src/components/ApiTester';
1313

1414
Request a scoped API key
1515

16-
Initiates the device-flow key request. The response contains an `approvalUrl` the user must visit to review and approve the requested scopes. Poll `GET /auth/key-request/{code}/status` until `status` is `approved` or `denied`.
16+
Initiates a key request that a user must approve in the dashboard. Supports two flows:
17+
18+
**Device flow (no `callbackUrl`)** — for CLI tools, scripts, and desktop apps. The response contains an `approvalUrl` to direct the user to. Poll `GET /auth/key-request/{code}/status` until `status` is `approved`, then read `apiKey` from the response.
19+
20+
**Web flow (`callbackUrl` provided)** — for web apps that can receive an HTTP redirect. After the user approves, the dashboard redirects to your `callbackUrl` with a `code` query parameter containing the exchange code. POST that code to `POST /auth/key-request/exchange` to retrieve the API key.
1721

1822
### Parameters
1923

@@ -23,7 +27,7 @@ Initiates the device-flow key request. The response contains an `approvalUrl` th
2327
| scopes | array || body | List of permission scopes the key requires |
2428
| appDescription | string | | body | Short description of what the application does |
2529
| appUrl | string | | body | Homepage or docs URL for the application |
26-
| callbackUrl | string | | body | URL to redirect to after approval (web flow) |
30+
| callbackUrl | string | | body | Enables web flow: URL the dashboard redirects to after approval, with a `code` query parameter containing the exchange code |
2731
| clientIds | array | | body | Foundry client IDs to restrict the key to |
2832
| suggestedMonthlyLimit | number | | body | Suggested per-key monthly request cap (user may override) |
2933
| suggestedExpiry | string | | body | Suggested expiry date ISO 8601 (user may override) |
@@ -180,9 +184,9 @@ import axios from 'axios';
180184

181185
```json
182186
{
183-
"approvalUrl": "http://localhost:3010/approve/F4JQJX",
184-
"code": "F4JQJX",
185-
"expiresAt": "2026-05-14T15:28:09-05:00",
187+
"approvalUrl": "http://localhost:3010/approve/EH789F",
188+
"code": "EH789F",
189+
"expiresAt": "2026-05-14T21:44:06-05:00",
186190
"expiresIn": 600
187191
}
188192
```
@@ -194,7 +198,7 @@ import axios from 'axios';
194198

195199
Poll key request status
196200

197-
Returns the current status of a pending key request. When `status` is `approved`, the response includes the newly created `key`. Once the key has been retrieved, the code is invalidated.
201+
Returns the current status of a pending key request. When `status` is `approved`, the response includes the newly created `apiKey`. Once the key has been retrieved, the status becomes `exchanged` and the key is no longer returned.
198202

199203
### Parameters
200204

@@ -204,7 +208,7 @@ Returns the current status of a pending key request. When `status` is `approved`
204208

205209
### Returns
206210

207-
**object** - `status` (`pending` | `approved` | `denied` | `expired`), `key` (when approved)
211+
**object** - `status` (`pending` | `approved` | `denied` | `expired` | `exchanged`), `apiKey`, `scopes`, `clientIds` (when approved)
208212

209213
### Try It Out
210214

@@ -221,7 +225,7 @@ Returns the current status of a pending key request. When `status` is `approved`
221225

222226
```javascript
223227
const baseUrl = 'http://localhost:3010';
224-
const path = '/auth/key-request/F4JQJX/status';
228+
const path = '/auth/key-request/EH789F/status';
225229
const url = `${baseUrl}${path}`;
226230

227231
const response = await fetch(url, {
@@ -235,7 +239,7 @@ console.log(data);
235239
<TabItem value="curl" label="cURL">
236240

237241
```bash
238-
curl -X GET 'http://localhost:3010/auth/key-request/F4JQJX/status'
242+
curl -X GET 'http://localhost:3010/auth/key-request/EH789F/status'
239243
```
240244

241245
</TabItem>
@@ -245,7 +249,7 @@ curl -X GET 'http://localhost:3010/auth/key-request/F4JQJX/status'
245249
import requests
246250

247251
base_url = 'http://localhost:3010'
248-
path = '/auth/key-request/F4JQJX/status'
252+
path = '/auth/key-request/EH789F/status'
249253
url = f'{base_url}{path}'
250254

251255
response = requests.get(
@@ -263,7 +267,7 @@ import axios from 'axios';
263267

264268
(async () => {
265269
const baseUrl = 'http://localhost:3010';
266-
const path = '/auth/key-request/F4JQJX/status';
270+
const path = '/auth/key-request/EH789F/status';
267271
const url = `${baseUrl}${path}`;
268272

269273
const response = await axios({
@@ -289,10 +293,10 @@ import axios from 'axios';
289293
💭 Connection settings
290294
🔤localhost🔤 ➡️ host
291295
3010 ➡️ port
292-
🔤/auth/key-request/F4JQJX/status🔤 ➡️ path
296+
🔤/auth/key-request/EH789F/status🔤 ➡️ path
293297
294298
💭 Build HTTP request
295-
🔤GET /auth/key-request/F4JQJX/status HTTP/1.1❌r❌nHost: localhost:3010❌r❌n❌r❌n🔤 ➡️ request
299+
🔤GET /auth/key-request/EH789F/status HTTP/1.1❌r❌nHost: localhost:3010❌r❌n❌r❌n🔤 ➡️ request
296300
297301
💭 Connect and send
298302
🍺 🆕📞 host port❗ ➡️ socket
@@ -327,3 +331,160 @@ import axios from 'axios';
327331
```
328332

329333

334+
---
335+
336+
## POST /auth/key-request/exchange
337+
338+
Exchange approval code for API key (web flow)
339+
340+
Web flow only. After the user approves the request in the dashboard, the relay redirects to the `callbackUrl` with a one-time `code` query parameter. POST that code here to receive the API key. The code is single-use — a second call returns 410.
341+
342+
### Parameters
343+
344+
| Name | Type | Required | Source | Description |
345+
|------|------|----------|--------|-------------|
346+
| code | string || body | The exchange code delivered to your callbackUrl |
347+
348+
### Returns
349+
350+
**object** - `apiKey`, `scopes`, `clientIds`
351+
352+
### Try It Out
353+
354+
<ApiTester
355+
method="POST"
356+
path="/auth/key-request/exchange"
357+
parameters={[{"name":"code","type":"string","required":true,"source":"body"}]}
358+
/>
359+
360+
### Code Examples
361+
362+
<Tabs groupId="programming-language">
363+
<TabItem value="javascript" label="JavaScript">
364+
365+
```javascript
366+
const baseUrl = 'http://localhost:3010';
367+
const path = '/auth/key-request/exchange';
368+
const url = `${baseUrl}${path}`;
369+
370+
const response = await fetch(url, {
371+
method: 'POST',
372+
headers: {
373+
'Content-Type': 'application/json'
374+
},
375+
body: JSON.stringify({
376+
"code": "your-exchange-code-here"
377+
})
378+
});
379+
const data = await response.json();
380+
console.log(data);
381+
```
382+
383+
</TabItem>
384+
<TabItem value="curl" label="cURL">
385+
386+
```bash
387+
curl -X POST 'http://localhost:3010/auth/key-request/exchange' \
388+
-H "Content-Type: application/json" \
389+
-d '{"code": "your-exchange-code-here"}'
390+
```
391+
392+
</TabItem>
393+
<TabItem value="python" label="Python">
394+
395+
```python
396+
import requests
397+
398+
base_url = 'http://localhost:3010'
399+
path = '/auth/key-request/exchange'
400+
url = f'{base_url}{path}'
401+
402+
response = requests.post(
403+
url,
404+
json={
405+
"code": "your-exchange-code-here"
406+
}
407+
)
408+
data = response.json()
409+
print(data)
410+
```
411+
412+
</TabItem>
413+
<TabItem value="typescript" label="TypeScript">
414+
415+
```typescript
416+
import axios from 'axios';
417+
418+
(async () => {
419+
const baseUrl = 'http://localhost:3010';
420+
const path = '/auth/key-request/exchange';
421+
const url = `${baseUrl}${path}`;
422+
423+
const response = await axios({
424+
method: 'post',
425+
headers: {
426+
'Content-Type': 'application/json'
427+
},
428+
url,
429+
data: {
430+
"code": "your-exchange-code-here"
431+
}
432+
});
433+
const data = response.data;
434+
console.log(data);
435+
})();
436+
```
437+
438+
</TabItem>
439+
<TabItem value="emojicode" label="Emojicode">
440+
441+
```emojicode
442+
📦 sockets 🏠
443+
444+
💭 Emojicode HTTP Client
445+
💭 Compile: emojicodec example.🍇 -o example
446+
💭 Run: ./example
447+
448+
🏁 🍇
449+
💭 Connection settings
450+
🔤localhost🔤 ➡️ host
451+
3010 ➡️ port
452+
🔤/auth/key-request/exchange🔤 ➡️ path
453+
454+
💭 Request body
455+
🔤{"code": "your-exchange-code-here"}🔤 ➡️ body
456+
457+
💭 Build HTTP request
458+
🔤POST /auth/key-request/exchange HTTP/1.1❌r❌nHost: localhost:3010❌r❌nContent-Type: application/json❌r❌nContent-Length: 43❌r❌n❌r❌n{"code": "your-exchange-code-here"}🔤 ➡️ request
459+
460+
💭 Connect and send
461+
🍺 🆕📞 host port❗ ➡️ socket
462+
🍺 💬 socket 📇 request❗❗
463+
464+
💭 Read and print response
465+
🍺 👂 socket 4096❗ ➡️ data
466+
😀 🍺 🔡 data❗❗
467+
468+
💭 Close socket
469+
🚪 socket❗
470+
🍉
471+
```
472+
473+
</TabItem>
474+
</Tabs>
475+
476+
#### Response
477+
478+
**Status:** 200
479+
480+
```json
481+
{
482+
"apiKey": "your-api-key-here",
483+
"clientIds": null,
484+
"scopes": [
485+
"entity:read"
486+
]
487+
}
488+
```
489+
490+

0 commit comments

Comments
 (0)