IDP User Metadata
Create and replace application-owned JSON metadata on company-scoped Sentinel identities.
IDP User Metadata
Each Sentinel identity can carry an application-owned JSON object. Metadata is returned by the full user profile and after successful magic-link verification, but it never grants Donutwork backoffice roles or API permissions.
All operations require a bearer API key with idp:write and remain scoped to its company.
Supply metadata during provisioning
Add the optional metadata object to the normal user provisioning request:
{
"user": {
"external_id": "ORG_USR_9921_AFG",
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": ["sso", "audit"]
}
}
}Replace metadata
idp:writePath, query, and header parameters
externalUserIdRequiredRequest Body
{
"user": {
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": [
"sso",
"audit"
]
}
}
}user.metadataRequiredResponses
Metadata replaced.
{
"user": {
"id": "6908...",
"external_id": "ORG_USR_9921_AFG",
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": [
"sso",
"audit"
]
}
}
}The identity does not exist in the authenticated company.
The metadata is not a valid object or exceeds a structural limit.
Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/idp/EXTERNALUSERID/metadata.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"user": {
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": [
"sso",
"audit"
]
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/idp/EXTERNALUSERID/metadata.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"user": {
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": [
"sso",
"audit"
]
}
}
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/idp/EXTERNALUSERID/metadata.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"user": {
"metadata": {
"plan": "enterprise",
"region": "eu",
"features": [
"sso",
"audit"
]
}
}
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
This endpoint replaces the full object; it does not merge fields. The object may contain at most 32 KB, 6 nested levels, and 500 entries. Keys must be 1–128 characters, cannot contain ., and cannot start with $. Values must be JSON-compatible.
The Public API logger redacts the complete metadata field from request and response content.