Re-mint a Client Workspace Key
curl --request POST \
--url https://app.tuco.ai/api/agency/workspace-keys \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.tuco.ai/api/agency/workspace-keys"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.tuco.ai/api/agency/workspace-keys', 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://app.tuco.ai/api/agency/workspace-keys",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/agency/workspace-keys"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.tuco.ai/api/agency/workspace-keys")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/agency/workspace-keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyAgency
Re-mint a Client Workspace Key
Mint a fresh workspace API key for any client in your agency — for a workspace created before keys were returned, or one whose key was lost or leaked.
POST
/
api
/
agency
/
workspace-keys
Re-mint a Client Workspace Key
curl --request POST \
--url https://app.tuco.ai/api/agency/workspace-keys \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.tuco.ai/api/agency/workspace-keys"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.tuco.ai/api/agency/workspace-keys', 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://app.tuco.ai/api/agency/workspace-keys",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/agency/workspace-keys"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.tuco.ai/api/agency/workspace-keys")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/agency/workspace-keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyCreating a workspace already
returns its key in the same response, which covers onboarding. This endpoint
covers the two cases that happen afterwards: a client created before that
existed, and a key that was lost or leaked.
Endpoint
- Method:
POSTto mint,GETto list what a client already has - Path:
/api/agency/workspace-keys - Auth:
Authorization: Bearer tucoagency_xxxxxxxxxxxxx(agency key)
Body
| Field | Type | Required | Notes |
|---|---|---|---|
workspaceId | string | yes | A client clerkOrgId. Must be in your agency |
name | string | no | Shown in that client’s dashboard. Defaults to Re-minted by agency. Truncated at 120 characters |
revokeExisting | boolean | no | Default false. See below — this is the field that matters |
Minting
curl https://app.tuco.ai/api/agency/workspace-keys \
-H "Authorization: Bearer tucoagency_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"workspaceId": "org_3KKJKupHJ8Y9I2NXVAga0c6ZqgT",
"name": "Acme Roofing — backend"
}'
{
"id": "6ac52ed996359b3643c71939",
"workspaceId": "org_3KKJKupHJ8Y9I2NXVAga0c6ZqgT",
"key": "tuco_xCsfhTiXXXXXXXXXXXXXXXXXXXXXXXX",
"name": "Acme Roofing — backend",
"revokedExisting": 0,
"warning": "Save this key now. You will not be able to see it again."
}
201 Created. The key is shown once — Tuco stores only its hash.
It is a normal workspace key: it works on every documented endpoint for that
one workspace, and on no other. It is not an agency key and cannot reach any
agency endpoint.
Additive by default
Minting does not revoke what is already there. The common case is “we lost our copy”, and silently killing a live integration to solve that is worse than the problem. So a new key is added alongside the existing ones and nothing breaks. -d '{ "workspaceId": "org_…", "revokeExisting": true }'
revokeExisting: true for the leaked-key case, where killing it is the
point. Every active key on that workspace is deactivated before the new one
is created, so there is no window in which the leaked key and its replacement
are both live. The count comes back as revokedExisting.
revokeExisting: true breaks any integration still using an old key — the
client’s own dashboard key included. Use it when a key has leaked, not as
routine hygiene.What a client already has
curl "https://app.tuco.ai/api/agency/workspace-keys?workspaceId=org_3KKJKupHJ8Y9I2NXVAga0c6ZqgT" \
-H "Authorization: Bearer tucoagency_xxxxxxxxxxxxx"
{
"agencyId": "user_3H08IRicrVUYLDZRyOh4jKRVwrx",
"workspaceId": "org_3KKJKupHJ8Y9I2NXVAga0c6ZqgT",
"apiKeys": [
{
"id": "6ac52ed996359b3643c71939",
"name": "Acme Roofing — backend",
"isActive": true,
"lastUsedAt": "2026-10-06T19:04:11.882Z",
"createdAt": "2026-10-06T18:55:02.117Z"
}
]
}
lastUsedAt is the useful
read before revoking: a key that has never been used (null) is safe to kill.
Agency keys are deliberately excluded from this list. It reports workspace keys
only.
Error responses
| Status | Code | When |
|---|---|---|
400 | WORKSPACE_REQUIRED | workspaceId missing. There is no default |
401 | UNAUTHORIZED | Missing, unknown, revoked or expired key |
403 | AGENCY_KEY_REQUIRED | A workspace key (tuco_…) was used |
403 | NOT_YOUR_WORKSPACE | That workspace is not part of your agency |