Migrate API key(s)
Returns HTTP 200 even on partial success; hashes that could not be migrated are listed under data.failed.
Required Permissions Your root key must have one of the following permissions for basic key information:
api.*.create_key(to migrate keys to any API)api.<api_id>.create_key(to migrate keys to a specific API)
package main
import(
"context"
"os"
unkey "github.com/unkeyed/sdks/api/go/v3"
"github.com/unkeyed/sdks/api/go/v3/models/components"
"log"
)
func main() {
ctx := context.Background()
s := unkey.New(
unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
)
res, err := s.Keys.MigrateKeys(ctx, components.V2KeysMigrateKeysRequestBody{
MigrationID: "your_company",
APIID: "api_123456789",
Keys: []components.V2KeysMigrateKeyData{
components.V2KeysMigrateKeyData{
Hash: "your_already_hashed_key",
Name: unkey.Pointer("Payment Service Production Key"),
ExternalID: unkey.Pointer("user_1234abcd"),
Meta: map[string]any{
"plan": "enterprise",
"featureFlags": map[string]any{
"betaAccess": true,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": map[string]any{
"tier": "premium",
"renewal": "2024-12-31",
},
},
Roles: []string{
"api_admin",
"billing_reader",
},
Permissions: []string{
"documents.read",
"documents.write",
"settings.view",
},
Credits: &components.KeyCreditsData{
Remaining: unkey.Pointer[int64](1000),
Refill: &components.KeyCreditsRefill{
Interval: components.KeyCreditsRefillIntervalDaily,
Amount: 1000,
RefillDay: unkey.Pointer[int64](15),
},
},
Ratelimits: []components.RatelimitRequest{
components.RatelimitRequest{
Name: "requests",
Limit: 100,
Duration: 60000,
AutoApply: unkey.Pointer(true),
},
components.RatelimitRequest{
Name: "heavy_operations",
Limit: 10,
Duration: 3600000,
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
if res.V2KeysMigrateKeysResponseBody != nil {
// handle response
}
}from unkey.py import Unkey, models
with Unkey(
root_key="<YOUR_BEARER_TOKEN_HERE>",
) as unkey:
res = unkey.keys.migrate_keys(migration_id="your_company", api_id="api_123456789", keys=[
{
"hash": "your_already_hashed_key",
"name": "Payment Service Production Key",
"external_id": "user_1234abcd",
"meta": {
"plan": "enterprise",
"featureFlags": {
"betaAccess": True,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31",
},
},
"roles": [
"api_admin",
"billing_reader",
],
"permissions": [
"documents.read",
"documents.write",
"settings.view",
],
"credits": {
"remaining": 1000,
"refill": {
"interval": models.KeyCreditsRefillInterval.DAILY,
"amount": 1000,
"refill_day": 15,
},
},
"ratelimits": [
{
"name": "requests",
"limit": 100,
"duration": 60000,
"auto_apply": True,
},
{
"name": "heavy_operations",
"limit": 10,
"duration": 3600000,
},
],
},
])
# Handle response
print(res)import { Unkey } from "@unkey/api";
const unkey = new Unkey({
rootKey: process.env["UNKEY_ROOT_KEY"] ?? "",
});
async function run() {
const result = await unkey.keys.migrateKeys({
migrationId: "your_company",
apiId: "api_123456789",
keys: [
{
hash: "your_already_hashed_key",
name: "Payment Service Production Key",
externalId: "user_1234abcd",
meta: {
"plan": "enterprise",
"featureFlags": {
"betaAccess": true,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31",
},
},
roles: [
"api_admin",
"billing_reader",
],
permissions: [
"documents.read",
"documents.write",
"settings.view",
],
credits: {
remaining: 1000,
refill: {
interval: "daily",
amount: 1000,
refillDay: 15,
},
},
ratelimits: [
{
name: "requests",
limit: 100,
duration: 60000,
autoApply: true,
},
{
name: "heavy_operations",
limit: 10,
duration: 3600000,
},
],
},
],
});
console.log(result);
}
run();curl --request POST \
--url https://api.unkey.com/v2/keys.migrateKeys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"migrationId": "your_company",
"apiId": "api_123456789",
"keys": [
{
"hash": "your_already_hashed_key",
"name": "Payment Service Production Key",
"externalId": "user_1234abcd",
"meta": {
"plan": "enterprise",
"featureFlags": {
"betaAccess": true,
"concurrentConnections": 10
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31"
}
},
"roles": [
"api_admin",
"billing_reader"
],
"permissions": [
"documents.read",
"documents.write",
"settings.view"
],
"expires": 2051222400000,
"enabled": true,
"ratelimits": [
{
"name": "requests",
"limit": 100,
"duration": 60000,
"autoApply": true
},
{
"name": "heavy_operations",
"limit": 10,
"duration": 3600000,
"autoApply": false
}
]
}
]
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
migrationId: 'your_company',
apiId: 'api_123456789',
keys: [
{
hash: 'your_already_hashed_key',
name: 'Payment Service Production Key',
externalId: 'user_1234abcd',
meta: {
plan: 'enterprise',
featureFlags: {betaAccess: true, concurrentConnections: 10},
customerName: 'Acme Corp',
billing: {tier: 'premium', renewal: '2024-12-31'}
},
roles: ['api_admin', 'billing_reader'],
permissions: ['documents.read', 'documents.write', 'settings.view'],
expires: 2051222400000,
enabled: true,
ratelimits: [
{name: 'requests', limit: 100, duration: 60000, autoApply: true},
{name: 'heavy_operations', limit: 10, duration: 3600000, autoApply: false}
]
}
]
})
};
fetch('https://api.unkey.com/v2/keys.migrateKeys', 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.unkey.com/v2/keys.migrateKeys",
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([
'migrationId' => 'your_company',
'apiId' => 'api_123456789',
'keys' => [
[
'hash' => 'your_already_hashed_key',
'name' => 'Payment Service Production Key',
'externalId' => 'user_1234abcd',
'meta' => [
'plan' => 'enterprise',
'featureFlags' => [
'betaAccess' => true,
'concurrentConnections' => 10
],
'customerName' => 'Acme Corp',
'billing' => [
'tier' => 'premium',
'renewal' => '2024-12-31'
]
],
'roles' => [
'api_admin',
'billing_reader'
],
'permissions' => [
'documents.read',
'documents.write',
'settings.view'
],
'expires' => 2051222400000,
'enabled' => true,
'ratelimits' => [
[
'name' => 'requests',
'limit' => 100,
'duration' => 60000,
'autoApply' => true
],
[
'name' => 'heavy_operations',
'limit' => 10,
'duration' => 3600000,
'autoApply' => false
]
]
]
]
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.unkey.com/v2/keys.migrateKeys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"migrationId\": \"your_company\",\n \"apiId\": \"api_123456789\",\n \"keys\": [\n {\n \"hash\": \"your_already_hashed_key\",\n \"name\": \"Payment Service Production Key\",\n \"externalId\": \"user_1234abcd\",\n \"meta\": {\n \"plan\": \"enterprise\",\n \"featureFlags\": {\n \"betaAccess\": true,\n \"concurrentConnections\": 10\n },\n \"customerName\": \"Acme Corp\",\n \"billing\": {\n \"tier\": \"premium\",\n \"renewal\": \"2024-12-31\"\n }\n },\n \"roles\": [\n \"api_admin\",\n \"billing_reader\"\n ],\n \"permissions\": [\n \"documents.read\",\n \"documents.write\",\n \"settings.view\"\n ],\n \"expires\": 2051222400000,\n \"enabled\": true,\n \"ratelimits\": [\n {\n \"name\": \"requests\",\n \"limit\": 100,\n \"duration\": 60000,\n \"autoApply\": true\n },\n {\n \"name\": \"heavy_operations\",\n \"limit\": 10,\n \"duration\": 3600000,\n \"autoApply\": false\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unkey.com/v2/keys.migrateKeys")
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 \"migrationId\": \"your_company\",\n \"apiId\": \"api_123456789\",\n \"keys\": [\n {\n \"hash\": \"your_already_hashed_key\",\n \"name\": \"Payment Service Production Key\",\n \"externalId\": \"user_1234abcd\",\n \"meta\": {\n \"plan\": \"enterprise\",\n \"featureFlags\": {\n \"betaAccess\": true,\n \"concurrentConnections\": 10\n },\n \"customerName\": \"Acme Corp\",\n \"billing\": {\n \"tier\": \"premium\",\n \"renewal\": \"2024-12-31\"\n }\n },\n \"roles\": [\n \"api_admin\",\n \"billing_reader\"\n ],\n \"permissions\": [\n \"documents.read\",\n \"documents.write\",\n \"settings.view\"\n ],\n \"expires\": 2051222400000,\n \"enabled\": true,\n \"ratelimits\": [\n {\n \"name\": \"requests\",\n \"limit\": 100,\n \"duration\": 60000,\n \"autoApply\": true\n },\n {\n \"name\": \"heavy_operations\",\n \"limit\": 10,\n \"duration\": 3600000,\n \"autoApply\": false\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"meta": {
"requestId": "req_123"
},
"data": {
"migrated": [
{
"hash": "sha256_abc123def456",
"keyId": "key_2cGKbMxRyIzhCxo1Idjz8q"
}
],
"failed": [
"sha256_ghi789jkl012"
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found",
"errors": [
{
"location": "body.permissions[0].name",
"message": "Must be at least 3 characters long",
"fix": "Ensure the name uses only alphanumeric characters, underscores, and hyphens"
}
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}Authorizations
Unkey uses bearer tokens for authentication. Public integrations use root keys, while the dashboard proxy uses short-lived JWTs. To authenticate, include the token in the Authorization header of each request:
Authorization: Bearer unkey_123
Root keys have specific permissions attached to them, controlling what operations they can perform. Legacy permissions use tuple strings like api.*.create_key; resource permissions use Unkey Resource Names plus actions, like unkey:v1:ws_123:keyspaces/*#create_key.
Security best practices:
- Keep root keys secure and never expose them in client-side code
- Use different root keys for different environments
- Rotate keys periodically, especially after team member departures
- Create keys with minimal necessary permissions following least privilege principle
- Monitor key usage with audit logs.
Body
Response
Successfully migrated keys.
Was this page helpful?
package main
import(
"context"
"os"
unkey "github.com/unkeyed/sdks/api/go/v3"
"github.com/unkeyed/sdks/api/go/v3/models/components"
"log"
)
func main() {
ctx := context.Background()
s := unkey.New(
unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
)
res, err := s.Keys.MigrateKeys(ctx, components.V2KeysMigrateKeysRequestBody{
MigrationID: "your_company",
APIID: "api_123456789",
Keys: []components.V2KeysMigrateKeyData{
components.V2KeysMigrateKeyData{
Hash: "your_already_hashed_key",
Name: unkey.Pointer("Payment Service Production Key"),
ExternalID: unkey.Pointer("user_1234abcd"),
Meta: map[string]any{
"plan": "enterprise",
"featureFlags": map[string]any{
"betaAccess": true,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": map[string]any{
"tier": "premium",
"renewal": "2024-12-31",
},
},
Roles: []string{
"api_admin",
"billing_reader",
},
Permissions: []string{
"documents.read",
"documents.write",
"settings.view",
},
Credits: &components.KeyCreditsData{
Remaining: unkey.Pointer[int64](1000),
Refill: &components.KeyCreditsRefill{
Interval: components.KeyCreditsRefillIntervalDaily,
Amount: 1000,
RefillDay: unkey.Pointer[int64](15),
},
},
Ratelimits: []components.RatelimitRequest{
components.RatelimitRequest{
Name: "requests",
Limit: 100,
Duration: 60000,
AutoApply: unkey.Pointer(true),
},
components.RatelimitRequest{
Name: "heavy_operations",
Limit: 10,
Duration: 3600000,
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
if res.V2KeysMigrateKeysResponseBody != nil {
// handle response
}
}from unkey.py import Unkey, models
with Unkey(
root_key="<YOUR_BEARER_TOKEN_HERE>",
) as unkey:
res = unkey.keys.migrate_keys(migration_id="your_company", api_id="api_123456789", keys=[
{
"hash": "your_already_hashed_key",
"name": "Payment Service Production Key",
"external_id": "user_1234abcd",
"meta": {
"plan": "enterprise",
"featureFlags": {
"betaAccess": True,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31",
},
},
"roles": [
"api_admin",
"billing_reader",
],
"permissions": [
"documents.read",
"documents.write",
"settings.view",
],
"credits": {
"remaining": 1000,
"refill": {
"interval": models.KeyCreditsRefillInterval.DAILY,
"amount": 1000,
"refill_day": 15,
},
},
"ratelimits": [
{
"name": "requests",
"limit": 100,
"duration": 60000,
"auto_apply": True,
},
{
"name": "heavy_operations",
"limit": 10,
"duration": 3600000,
},
],
},
])
# Handle response
print(res)import { Unkey } from "@unkey/api";
const unkey = new Unkey({
rootKey: process.env["UNKEY_ROOT_KEY"] ?? "",
});
async function run() {
const result = await unkey.keys.migrateKeys({
migrationId: "your_company",
apiId: "api_123456789",
keys: [
{
hash: "your_already_hashed_key",
name: "Payment Service Production Key",
externalId: "user_1234abcd",
meta: {
"plan": "enterprise",
"featureFlags": {
"betaAccess": true,
"concurrentConnections": 10,
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31",
},
},
roles: [
"api_admin",
"billing_reader",
],
permissions: [
"documents.read",
"documents.write",
"settings.view",
],
credits: {
remaining: 1000,
refill: {
interval: "daily",
amount: 1000,
refillDay: 15,
},
},
ratelimits: [
{
name: "requests",
limit: 100,
duration: 60000,
autoApply: true,
},
{
name: "heavy_operations",
limit: 10,
duration: 3600000,
},
],
},
],
});
console.log(result);
}
run();curl --request POST \
--url https://api.unkey.com/v2/keys.migrateKeys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"migrationId": "your_company",
"apiId": "api_123456789",
"keys": [
{
"hash": "your_already_hashed_key",
"name": "Payment Service Production Key",
"externalId": "user_1234abcd",
"meta": {
"plan": "enterprise",
"featureFlags": {
"betaAccess": true,
"concurrentConnections": 10
},
"customerName": "Acme Corp",
"billing": {
"tier": "premium",
"renewal": "2024-12-31"
}
},
"roles": [
"api_admin",
"billing_reader"
],
"permissions": [
"documents.read",
"documents.write",
"settings.view"
],
"expires": 2051222400000,
"enabled": true,
"ratelimits": [
{
"name": "requests",
"limit": 100,
"duration": 60000,
"autoApply": true
},
{
"name": "heavy_operations",
"limit": 10,
"duration": 3600000,
"autoApply": false
}
]
}
]
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
migrationId: 'your_company',
apiId: 'api_123456789',
keys: [
{
hash: 'your_already_hashed_key',
name: 'Payment Service Production Key',
externalId: 'user_1234abcd',
meta: {
plan: 'enterprise',
featureFlags: {betaAccess: true, concurrentConnections: 10},
customerName: 'Acme Corp',
billing: {tier: 'premium', renewal: '2024-12-31'}
},
roles: ['api_admin', 'billing_reader'],
permissions: ['documents.read', 'documents.write', 'settings.view'],
expires: 2051222400000,
enabled: true,
ratelimits: [
{name: 'requests', limit: 100, duration: 60000, autoApply: true},
{name: 'heavy_operations', limit: 10, duration: 3600000, autoApply: false}
]
}
]
})
};
fetch('https://api.unkey.com/v2/keys.migrateKeys', 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.unkey.com/v2/keys.migrateKeys",
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([
'migrationId' => 'your_company',
'apiId' => 'api_123456789',
'keys' => [
[
'hash' => 'your_already_hashed_key',
'name' => 'Payment Service Production Key',
'externalId' => 'user_1234abcd',
'meta' => [
'plan' => 'enterprise',
'featureFlags' => [
'betaAccess' => true,
'concurrentConnections' => 10
],
'customerName' => 'Acme Corp',
'billing' => [
'tier' => 'premium',
'renewal' => '2024-12-31'
]
],
'roles' => [
'api_admin',
'billing_reader'
],
'permissions' => [
'documents.read',
'documents.write',
'settings.view'
],
'expires' => 2051222400000,
'enabled' => true,
'ratelimits' => [
[
'name' => 'requests',
'limit' => 100,
'duration' => 60000,
'autoApply' => true
],
[
'name' => 'heavy_operations',
'limit' => 10,
'duration' => 3600000,
'autoApply' => false
]
]
]
]
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.unkey.com/v2/keys.migrateKeys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"migrationId\": \"your_company\",\n \"apiId\": \"api_123456789\",\n \"keys\": [\n {\n \"hash\": \"your_already_hashed_key\",\n \"name\": \"Payment Service Production Key\",\n \"externalId\": \"user_1234abcd\",\n \"meta\": {\n \"plan\": \"enterprise\",\n \"featureFlags\": {\n \"betaAccess\": true,\n \"concurrentConnections\": 10\n },\n \"customerName\": \"Acme Corp\",\n \"billing\": {\n \"tier\": \"premium\",\n \"renewal\": \"2024-12-31\"\n }\n },\n \"roles\": [\n \"api_admin\",\n \"billing_reader\"\n ],\n \"permissions\": [\n \"documents.read\",\n \"documents.write\",\n \"settings.view\"\n ],\n \"expires\": 2051222400000,\n \"enabled\": true,\n \"ratelimits\": [\n {\n \"name\": \"requests\",\n \"limit\": 100,\n \"duration\": 60000,\n \"autoApply\": true\n },\n {\n \"name\": \"heavy_operations\",\n \"limit\": 10,\n \"duration\": 3600000,\n \"autoApply\": false\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unkey.com/v2/keys.migrateKeys")
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 \"migrationId\": \"your_company\",\n \"apiId\": \"api_123456789\",\n \"keys\": [\n {\n \"hash\": \"your_already_hashed_key\",\n \"name\": \"Payment Service Production Key\",\n \"externalId\": \"user_1234abcd\",\n \"meta\": {\n \"plan\": \"enterprise\",\n \"featureFlags\": {\n \"betaAccess\": true,\n \"concurrentConnections\": 10\n },\n \"customerName\": \"Acme Corp\",\n \"billing\": {\n \"tier\": \"premium\",\n \"renewal\": \"2024-12-31\"\n }\n },\n \"roles\": [\n \"api_admin\",\n \"billing_reader\"\n ],\n \"permissions\": [\n \"documents.read\",\n \"documents.write\",\n \"settings.view\"\n ],\n \"expires\": 2051222400000,\n \"enabled\": true,\n \"ratelimits\": [\n {\n \"name\": \"requests\",\n \"limit\": 100,\n \"duration\": 60000,\n \"autoApply\": true\n },\n {\n \"name\": \"heavy_operations\",\n \"limit\": 10,\n \"duration\": 3600000,\n \"autoApply\": false\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"meta": {
"requestId": "req_123"
},
"data": {
"migrated": [
{
"hash": "sha256_abc123def456",
"keyId": "key_2cGKbMxRyIzhCxo1Idjz8q"
}
],
"failed": [
"sha256_ghi789jkl012"
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found",
"errors": [
{
"location": "body.permissions[0].name",
"message": "Must be at least 3 characters long",
"fix": "Ensure the name uses only alphanumeric characters, underscores, and hyphens"
}
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}