Enterprise user provisioning API
Provision, update and deprovision enterprise users in batches. The integration token determines the tenant, so callers never supply an organization identifier.
1. Authenticate your integration
An enterprise administrator manages tokens through /api/enterprise/integration-tokens. Store the returned vdt_… secret in a secret manager: it is shown only once.
Every request requires
Authorization: Bearer vdt_your_secret_token. A disabled or expired token receives 401 Unauthorized. A token is valid only as long as the enterprise that issued it exists and is open: once that enterprise is deleted, purged after an expired licence, suspended or deactivated, its tokens stop working and receive the same 401 Unauthorized, so the status never tells a client whether an enterprise exists or what state it is in. Restoring the enterprise to an open state makes its existing secrets work again, with no new token to issue.Activating or revoking a token is a PATCH on that same endpoint, carrying is_active. The field is required and accepts the JSON booleans true and false, the strings "true" and "false", and the integers 1 and 0. Any other value is refused with 400 invalid_is_active, and a request without is_active is refused too, so a malformed body can never revoke a token.
Sending the state a token already has succeeds and changes nothing: the first revocation date is kept and no audit entry is written. A
404 therefore means the identifier belongs to no token of the tenant.2. List users
/api/integration/usersReturns only users belonging to the tenant associated with the token. For linked members, email is the current global identity address and can be null. The display name, role and account status belong to the membership in this tenant.
curl --request GET "https://visidog.com/api/integration/users" \
--header "Authorization: Bearer vdt_your_secret_token"200 response{
"users": [{
"user_id": "user_01J…", "email": "[email protected]",
"display_name": "Ada Lovelace", "role": "user", "status": "active",
"is_active": 1, "config_id": "cfg_01J…", "access_role_id": 12,
"valid_from": "2026-09-20 09:00:00", "valid_until": "2026-12-31 23:59:59"
}]
}3. Create users awaiting confirmation
/api/integration/usersCreation is batch-oriented. Each account receives a new global identity and waits for email confirmation. A valid email address is required. Existing accounts are never merged or attached by email; invite an existing identity through the portal. The integration sends an activation email after the account and its audit entry are committed. No password is issued by the provisioning API.
Requestcurl --request POST "https://visidog.com/api/integration/users" \
--header "Authorization: Bearer vdt_your_secret_token" \
--header "Content-Type: application/json" \
--data '{"users":[{"display_name":"Ada Lovelace","email":"[email protected]","config_id":"cfg_01J…","access_role_id":12,"role":"user"}]}'200 response{ "results": [{ "user_id": "user_01J…", "status": "created", "account_status": "pending", "is_active": false, "activation_sent": true, "activation_url": null }] }A pending account reserves a license seat.
activation_url is null in ordinary responses: the link goes to the email recipient. If activation_sent is false, the account remains pending and an administrator can resend its link in the portal. The recipient confirms the address, then uses password recovery to choose a first password before signing in.Creation is refused with
403 license_expired when the company license is expired. At quota, the corresponding item returns license_quota_exceeded.An omitted or
null access_role_id takes the default role of your enterprise; an item that names a role of another enterprise, a role that does not exist or a value that is not a role identifier fails with invalid_access_role, and an enterprise without a default role fails with default_access_role_missing.4. Update or delete users
/api/integration/userscurl --request PATCH "https://visidog.com/api/integration/users" \
--header "Authorization: Bearer vdt_your_secret_token" \
--header "Content-Type: application/json" \
--data '{"users":[{"user_id":"user_01J…","display_name":"Ada L.","is_active":true}]}'/api/integration/userscurl --request DELETE "https://visidog.com/api/integration/users" \
--header "Authorization: Bearer vdt_your_secret_token" \
--header "Content-Type: application/json" \
--data '{"users":[{"user_id":"user_01J…"}]}'role only accepts user, admin and admin-light; any other value fails that item with invalid_role, and an omitted role means user. An item whose role or is_active would remove the tenant's last active administrator — including a delete of that account — fails with last_administrator_required. Promote or reactivate another account first, then retry.Setting
is_active: true cannot activate a pending account or a global identity without a confirmed address. The item fails with account_not_verified, including after a prior deactivation. Confirm the address first. All fields of a refused item remain unchanged.access_role_id must designate a role of your own enterprise, on POST as on PATCH. Any other value fails that item with invalid_access_role, which covers a role that does not exist, a role of another enterprise, and a value that is not a role identifier at all. A creation that names no role takes your enterprise's default role, and a creation in an enterprise that has none fails with default_access_role_missing. On PATCH, null removes the explicit assignment: the account then follows the default role of your enterprise, as the enterprise portal does. The audit entry of a successful assignment records the role that was written, not the string that was sent.5. Batch responses and audit
A mutation returns one result per submitted item. Successful operations are recorded individually and the complete batch is also written to the enterprise audit trail.
createdNew account awaiting email confirmation, with its delivery status.updated / deletedOperation completed for the supplied user ID.errorOnly that item failed; inspect its stable code and safely retry the remaining items.Never put tokens in URLs, browser code, logs or source control. Rotate a token by creating a replacement, updating your integration secret, then disabling the old token.
6. Error codes and support correlation
An item with status: error carries a stable code. Business refusals use an explicit vocabulary: invalid_role, missing_required_fields, email_already_exists, account_invitation_required, account_not_verified, license_expired, invalid_config_id, invalid_access_role, default_access_role_missing, license_quota_exceeded, user_id_required, no_mutable_field, not_found and last_administrator_required. An audit failure uses audit_trail_unavailable.
A technical failure uses integration_user_failed, a neutral error message and a request_id. Supply that identifier to support to locate the server diagnostic. SQL details, internal paths and exception messages are not returned. A successful created result never carries a personal code or its hash. account_status describes the pending account and activation_sent reports email delivery.