> ## Documentation Index
> Fetch the complete documentation index at: https://docs.turnkey.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Turnkey is wallet infrastructure: create and manage crypto wallets, sign transactions, and enforce policy-based access controls. Best-fit uses: embedded consumer wallets (email/passkey/social auth, no seed phrases), automated onchain operations with server-side wallets, AI agent wallets with policy-scoped signing, enterprise key management, and verifiable off-chain workloads on Turnkey Verifiable Cloud (TVC).
> Every API call is a JSON POST to https://api.turnkey.com signed with a P-256 API key; create an organization and key self-serve at https://app.turnkey.com.
> Key Turnkey developer resources: API reference (https://docs.turnkey.com/api-reference/overview/intro.md), OpenAPI spec (https://docs.turnkey.com/public_api.swagger.json), authentication (https://docs.turnkey.com/features/authentication/overview.md), webhooks (https://docs.turnkey.com/features/webhooks/overview.md), MCP server for docs search (https://docs.turnkey.com/mcp), skill for building with Turnkey (https://docs.turnkey.com/skill.md), agent skills for operating an organization (https://docs.turnkey.com/get-started/ai-skills.md), CLI (https://docs.turnkey.com/sdks/cli.md), SDK reference (https://docs.turnkey.com/sdks/introduction.md), full docs content (https://docs.turnkey.com/llms-full.txt).

# Mint JWT

> Turn a Turnkey policy decision into a short-lived JWT that authorizes one exact HTTP request to your service.

> [!WARNING]
>
> Mint JWT is in beta and is enabled per organization.
> [Contact sales](https://www.turnkey.com/contact-us) to turn it on.

Mint JWT lets your service rely on Turnkey's [policy engine](https://docs.turnkey.com/features/policies/overview.md) to decide who may call it. A user asks Turnkey to sign a JWT for one HTTP request. Turnkey applies your policies, including any approvals they require, and returns a token that your service verifies with Turnkey's public keys.

The token is bound to one exact request: method, path, query, chosen headers, and body. It can't be reused for a different request, and it expires within five minutes.

## How it works

1. Your client builds the HTTP request it wants to send, then submits `ACTIVITY_TYPE_MINT_JWT` with a digest of that request.
2. Turnkey evaluates your policies. If they need more approvals, the activity waits for them.
3. Turnkey signs the JWT with ES256 and returns it.
4. Your client sends the request with the JWT in the header. Your service checks the signature and that the token matches the request.

## Who can mint

Your [policies](https://docs.turnkey.com/features/policies/overview.md) decide who can mint and who must approve. As with any activity, the [root quorum](https://docs.turnkey.com/features/users/root-quorum.md) can mint without a policy, and every other user needs one.

> [!NOTE]
>
> The `jwt.profile.httpz.audience`, `jwt.profile.httpz.method`, and `jwt.profile.httpz.path` policy fields are coming soon.

A trading service can mint order tokens on its own:

```json theme={"system"}
{
  "policyName": "Trading service can mint order tokens",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(u, u.id == '<TRADING_SERVICE_USER_ID>')",
  "condition": "activity.type == 'ACTIVITY_TYPE_MINT_JWT' &&
    jwt.profile.httpz.audience == 'https://app.example.com' &&
    jwt.profile.httpz.method == 'POST' &&
    jwt.profile.httpz.path == '/orders'"
}
```

Withdrawal tokens also need two administrators:

```json theme={"system"}
{
  "policyName": "Withdrawal tokens need two administrators",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(u, u.id == '<TRADING_SERVICE_USER_ID>') &&
    approvers.filter(u, u.tags.contains('<ADMIN_TAG_ID>')).count() >= 2",
  "condition": "activity.type == 'ACTIVITY_TYPE_MINT_JWT' &&
    jwt.profile.httpz.audience == 'https://app.example.com' &&
    jwt.profile.httpz.method == 'POST' &&
    jwt.profile.httpz.path == '/withdrawals'"
}
```

## Mint a token

```typescript theme={"system"}
import { createHash } from "node:crypto";

const body = JSON.stringify({ asset: "USDT", amount: "250", address: "0x1111...1111" });
const digest = (data: string) => `sha-256=:${createHash("sha256").update(data).digest("base64")}:`;

const { jwt } = await turnkey.apiClient().mintJwt({
  profile: "JWT_PROFILE_HTTP_AUTHZ_V1",
  audience: "https://app.example.com",
  httpAuthzV1: {
    method: "POST",
    path: "/withdrawals",
    query: "",
    bodyDigest: digest(body),
    subject: userId,
    timeToLiveSeconds: "60",
    issuedAt: String(Math.floor(Date.now() / 1000)),
  },
});

await fetch("https://app.example.com/withdrawals", {
  method: "POST",
  headers: { authorization: `Bearer ${jwt}`, "content-type": "application/json" },
  body,
});
```

To bind headers too, add `headerNames` and one `headerDigest` per name. You can't bind `authorization` or hop-by-hop headers. See the [Mint JWT API reference](https://docs.turnkey.com/api-reference/activities/mint-jwt.md) for every field.

## Token claims

The `turnkey.io/http-authz/v1` profile produces these claims:

| Claim | Value |
| :- | :- |
| `iss` | `https://id.turnkey.com/t/<organization ID>`. For a [sub-organization](https://docs.turnkey.com/features/sub-organizations.md), this is the parent organization |
| `org` | `urn:turnkey:org:<organization ID>`, the organization that minted the token |
| `sub` | `urn:turnkey:user:<user ID>` |
| `aud` | Your service's origin, such as `https://app.example.com` |
| `iat`, `exp` | Issue time and expiry. The lifetime is 1 to 300 seconds |
| `tk_request` | The bound `method`, `path`, `query`, and header names |
| `tk_body_digest` | SHA-256 digest of the body |
| `tk_header_digest` | SHA-256 digest of each bound header value |

## Verify a token

Your service checks each request:

1. **Signature.** Verify the ES256 signature with the key from `https://id.turnkey.com/t/<organization ID>/jwks.json`. Use the organization ID from `iss`.
2. **Issuer and audience.** `iss` is an organization you trust, and `aud` is your own origin.
3. **Time.** `exp` is in the future.
4. **Request.** The method, path, query, header digests, and body digest match the request you received.
5. **Subject.** `sub` and `org` are a user and organization allowed to make this request.

A token stays valid until it expires. To stop replays, reject a token you have already accepted until its `exp` passes.

In Rust, [`turnkey_jwt_profiles`](https://docs.rs/turnkey_jwt_profiles) runs all of these checks, with an optional `axum` middleware. The source is in [tkhq/rust-sdk](https://github.com/tkhq/rust-sdk/tree/main/jwt_profiles).

```rust theme={"system"}
use std::sync::Arc;
use turnkey_jwt_profiles::http_authz::jwks::PinnedKeySet;
use turnkey_jwt_profiles::http_authz::{unix_now, ReceivedRequest, RouteRule, Verifier, VerifierConfig};

// Build once at startup, with the JWKS for your organization.
let keys = PinnedKeySet::from_jwks_json(jwks)?;
let verifier = Verifier::new(VerifierConfig::new("https://app.example.com", ORGANIZATION_ID), Arc::new(keys))?;

// For each request:
let rule = RouteRule { required_headers: vec![], require_body_digest: true };
let received = ReceivedRequest { method: "POST", path: "/withdrawals", raw_query: None, headers: &headers, body: &body };
let claims = verifier.authorize(token, &rule, &received, unix_now())?;
// claims.sub and claims.org identify the caller.
```

## Next steps

![](https://docs.turnkey.com/images/solutions/light/tvc.svg)

JWT auth for TVC apps

Let Turnkey policy decide who can call your enclave app.

[Open](https://docs.turnkey.com/solutions/verifiable-cloud/jwt-http-auth.md)

Turnkey Verifiable Cloud

Deploy apps with verifiable code and secure keys.

[Open](https://docs.turnkey.com/features/verifiable-cloud/overview.md)

Policy Engine

Consensus expressions, conditions, and time windows.

[Open](https://docs.turnkey.com/features/policies/overview.md)

Root quorum

How root users and thresholds work.

[Open](https://docs.turnkey.com/features/users/root-quorum.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.