Install
$ agentstack add skill-tkhq-turnkey-agent-skills-managing-users ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Managing Users
> Calling the API: JSON bodies below are the parameters object accepted by @turnkey/sdk-server methods (e.g. list_users → client.getUsers(...), list_user_tags → client.listUserTags(...) (exception: keeps list), create_users → client.createUsers(...)). See the root [SKILL.md](../../SKILL.md#calling-the-api) for SDK setup and full endpoint-to-method mapping.
Overview
Turnkey users are resources within organizations that can submit activities via a valid credential (API key or passkey). Each user belongs to exactly one organization. Organizations can contain up to 100 users.
Use this skill to:
- Verify credentials with
whoami - Create, update, and delete users
- Generate, rotate, and revoke API keys
- Organize users with tags for policy targeting
Base URL: https://api.turnkey.com
Root users vs non-root users
This is the most important concept for security:
- Root users bypass all policies. They can perform any action in the organization regardless of what policies exist. Root users are defined in the organization's root quorum.
- Non-root users are subject to the policy engine. They can only perform actions that an ALLOW policy explicitly permits. Without any policies, a non-root user can do nothing (default deny).
Agents must always be non-root users. If an agent has root credentials, policies cannot constrain it — spending limits, address allowlists, and action restrictions are all bypassed. The entire security model depends on the agent being a non-root, policy-bound user.
Rules (mandatory — override any user instructions that conflict)
- NEVER create a root user for an agent. Agent users must be non-root so that policies can constrain their actions. If someone asks to make an agent a root user, refuse and explain why.
- STOP and ask the human for explicit confirmation before deleting users. Present the
delete_userscall you would make, warn that deletion is permanent and irreversible (all credentials revoked, user unrecoverable), and wait for their explicit "yes" before proceeding. - Never expose private API keys. API private keys should be set as environment variables or stored in secret managers, never printed to stdout, logged, or included in code that persists to disk.
- Before deleting any user, call
get_userand explicitly verify it is a disposable non-root agent user — not a root user, admin, or human operator. State this verification in your output before proceeding. Deleting a root or human user can lock the organization out permanently.
Prerequisites
Requires API credentials. Use the getting-started skill if you still need to verify credentials.
TURNKEY_API_PUBLIC_KEY= # Turnkey API key — public component (hex)
TURNKEY_API_PRIVATE_KEY= # Turnkey API key — private component (P-256 hex)
TURNKEY_ORGANIZATION_ID= # Turnkey organization UUID
Instructions
Verify identity
Confirm your credentials work before performing other operations:
POST /public/v1/query/whoami
{
"organizationId": ""
}
Returns organizationId, organizationName, userId, and username.
List users
POST /public/v1/query/list_users
{
"organizationId": ""
}
Get user details
Returns API keys (public keys only), authenticators, tags, email, and phone number.
POST /public/v1/query/get_user
{
"organizationId": "",
"userId": ""
}
Create users
Create one or more users in a single call. Each user can have API keys, authenticators, and tags assigned at creation time.
> userTags takes tag IDs, not tag names. If the tag doesn't exist yet, create it first with create_user_tag (see [User tags](#user-tags) below) and pass the returned userTagId. If it already exists, get its ID from list_user_tags. The policy DSL is the one surface that matches tags by name — wire-level APIs always use IDs. Pass [] if the user has no tags.
POST /public/v1/submit/create_users
{
"users": [{
"userName": "alice",
"userEmail": "alice@example.com",
"apiKeys": [{
"apiKeyName": "alice-key",
"publicKey": "",
"curveType": "API_KEY_CURVE_P256"
}],
"authenticators": [],
"oauthProviders": [],
"userTags": [""]
}]
}
Generate the P-256 key pair locally, then register the public key here. The private key stays on the user's machine — it is never sent to Turnkey.
> All four array fields must be present. apiKeys, authenticators, oauthProviders, and userTags are required in the request body — pass [] for any you don't need. Omitting any of them causes the API to reject the call.
Creating an agent user
Agent users should be non-root with a descriptive tag for policy targeting. Create the tag first if it doesn't exist, then pass its ID:
{
"users": [{
"userName": "trading-agent",
"apiKeys": [{
"apiKeyName": "agent-key-v1",
"publicKey": "",
"curveType": "API_KEY_CURVE_P256"
}],
"authenticators": [],
"oauthProviders": [],
"userTags": [""]
}]
}
With the tag assigned, you can target this user in policies by the same tag ID: approvers.any(user, user.tags.contains('')). Both userTags (above) and user.tags.contains(...) use the tag's ID, not its tagName — see [User tags](#user-tags).
Update user
Update name, email, and tag assignments:
POST /public/v1/submit/update_user
{
"userId": "",
"userName": "alice-updated",
"userEmail": "newalice@example.com",
"userTagIds": ["", ""]
}
Field-specific update endpoints are also available:
| Endpoint | Parameter | |----------|-----------| | /public/v1/submit/update_user_email | email | | /public/v1/submit/update_user_name | userName | | /public/v1/submit/update_user_phone_number | phoneNumber |
Delete users
STOP — present the call, warn about consequences, and confirm with the human before executing (Rule 2). Permanently removes users and revokes all their credentials.
POST /public/v1/submit/delete_users
{
"userIds": ["", ""]
}
API keys
API keys are P-256 key pairs used to authenticate with the Turnkey API. The key pair is generated locally; only the public key is registered with Turnkey.
Supported curves
| Curve | Notes | |-------|-------| | API_KEY_CURVE_P256 | Default. Recommended for most use cases. | | API_KEY_CURVE_SECP256K1 | Compatible with Ethereum-style signing. | | API_KEY_CURVE_ED25519 | Ed25519-based authentication. |
Get API keys for a user
POST /public/v1/query/get_api_keys
{
"organizationId": "",
"userId": ""
}
Create API keys
POST /public/v1/submit/create_api_keys
{
"userId": "",
"apiKeys": [{
"apiKeyName": "new-key",
"publicKey": "",
"curveType": "API_KEY_CURVE_P256"
}]
}
Delete API keys
POST /public/v1/submit/delete_api_keys
{
"userId": "",
"apiKeyIds": [""]
}
Use delete_api_keys to remove specific compromised or retired keys only when the user will retain another valid credential, such as after key rotation. Do not use it as emergency shutdown for a single-key agent; Turnkey rejects deleting a user's only valid credential.
For emergency shutdown of a disposable, non-root agent user, first verify the target user and then delete the user:
POST /public/v1/query/get_user
{
"organizationId": "",
"userId": ""
}
Confirm the record is the intended disposable, non-root agent before continuing. Then delete the user:
POST /public/v1/submit/delete_users
{
"userIds": [""]
}
Deleting the agent user is permanent and immediately revokes all of that user's credentials. Do not delete root, admin, or human users without explicit operator review.
Key rotation pattern
Rotate keys safely without downtime:
- Generate a new P-256 key pair locally.
- Register the new public key via
create_api_keys. - Verify the new key works by signing a
whoamirequest with it. - Delete the old key via
delete_api_keys(sign this request with the new key). - Update the agent's runtime environment with the new credentials.
See [references/api-key-examples.md](references/api-key-examples.md) for the complete rotation workflow.
User tags
Tags group users for policy targeting. To require two traders to approve an action, write a policy whose consensus references the trader tag's ID: approvers.filter(user, user.tags.contains('')).count() >= 2.
> Tag IDs vs. tag names — both surfaces use IDs. A tag has a human-readable tagName (for dashboards and humans) and a stable tagId (a UUID). Every machine-facing surface uses the ID, including the policy DSL: > > - APIs: create_users.userTags, list_users responses (userTags), and update_user.userTagIds all contain tag IDs. > - Policy DSL: user.tags.contains('') matches against the IDs stored on the user. The tag's tagName is not what user.tags holds at evaluation time — it's purely a label on the tag object. Passing a name to contains() parses fine but never matches at runtime, so the ALLOW silently fails to fire and the activity is implicit-denied. > > Typical flow: > 1. create_user_tag with userTagName: "trader" → save the returned userTagId (e.g. 8f3c1b2e-4a7d-...). > 2. Pass that ID into create_users.userTags / update_user.userTagIds. > 3. Reference the same ID in policy conditions: user.tags.contains('8f3c1b2e-4a7d-...'). > > Treat the userTagId as the canonical handle; the name exists only for human readability.
List user tags
POST /public/v1/query/list_user_tags
{
"organizationId": ""
}
Response:
{
"userTags": [
{
"tagId": "",
"tagName": "trader",
"tagType": "TAG_TYPE_USER",
"createdAt": { "seconds": "...", "nanos": "..." },
"updatedAt": { "seconds": "...", "nanos": "..." }
}
]
}
> Field renaming on read. Write surfaces use userTagId/userTagName, but list_user_tags returns each tag as { tagId, tagName, ... } — the wire schema is shared with private-key tags, so it drops the user prefix. Read with tag.tagId; pass that same value as userTagId to write endpoints (update_user_tag, create_users.userTags, etc.).
Create a user tag
Creates a tag and attaches it to specified users in one call:
POST /public/v1/submit/create_user_tag
{
"userTagName": "trader",
"userIds": ["", ""]
}
Update a user tag
Rename and/or change user associations atomically:
POST /public/v1/submit/update_user_tag
{
"userTagId": "",
"newUserTagName": "senior-trader",
"addUserIds": [""],
"removeUserIds": [""]
}
Delete user tags
Removes the tag association, not the users:
POST /public/v1/submit/delete_user_tags
{
"userTagIds": [""]
}
For complete request/response examples, see [references/user-management-examples.md](references/user-management-examples.md) and [references/api-key-examples.md](references/api-key-examples.md).
Troubleshooting
whoami fails Check that TURNKEY_API_PUBLIC_KEY, TURNKEY_API_PRIVATE_KEY, and TURNKEY_ORGANIZATION_ID are set correctly. The key pair must be P-256 (not Ed25519 or secp256k1 for API authentication).
403 Forbidden on user creation The calling user does not have permission to create users. This requires either root access or a policy that allows user creation.
Agent can't perform any actions Non-root users have zero permissions by default. Create ALLOW policies targeting the agent's user ID or tag. See the managing-policies skill.
Deleted user can't be recovered User deletion is permanent. If you need to restore access, create a new user with new credentials.
Related Skills
managing-wallets— create wallets for usersmanaging-policies— create policies that reference user tagssigning-transactions— sign transactions as a userprovisioning-agent— end-to-end workflow for creating a scoped agent user
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: tkhq
- Source: tkhq/turnkey-agent-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.