> ## Documentation Index
> Fetch the complete documentation index at: https://turnkey-0e7c1f5b-zeke-secrets-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Programmable Credential Access

> A password manager built for machines: policy-gated, programmable access to secrets for humans, services, and AI agents.

export const FeatureCard = ({title, description, icon, logo, href}) => {
  return <a href={href} className="not-prose font-normal group ring-0 ring-transparent cursor-pointer block rounded-lg border border-zinc-950/10 dark:border-white/10 bg-white dark:bg-transparent p-5 no-underline hover:border-primary/40 transition-colors">
      <div className="tk-card-row">
        <span className="tk-card-icon-wrap">
          {logo ? <img src={`/images/networks/${logo}.svg`} className="tk-card-network-logo" alt="" /> : <span className="tk-card-icon" style={{
    maskImage: `url(/images/icons/${icon}.svg)`,
    WebkitMaskImage: `url(/images/icons/${icon}.svg)`
  }} />}
        </span>
        <div>
          <div className="font-semibold text-sm text-zinc-950 dark:text-white group-hover:text-primary transition-colors">
            {title}
          </div>
          {description && <div className="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
              {description}
            </div>}
        </div>
      </div>
    </a>;
};

<Info>
  **Closed beta**: the Secrets API is currently in closed beta. [Contact us](https://www.turnkey.com/contact-us) to get onboarded.
</Info>

When an AI agent needs a password, a card number, or an API key, teams face an unpleasant choice: hand the agent unfettered access to credentials, or put a human in the loop for every single request. Turnkey gives you a third option: a programmable access layer that evaluates every credential request against policies you control, inside a [secure enclave](/security/secure-enclaves) that never releases plaintext unless the policy allows it.

Policies and tags are fully dynamic: update them at runtime to widen or narrow access on the fly, without re-importing anything. This solution builds on [Secret Storage](/features/secrets).

## Access patterns

One policy engine supports the full spectrum of trust models:

| Pattern                   | How it works                                                                                                                                           |
| :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Unilateral agent**      | A session key authenticates an ephemeral agent instance as a durable agent-role user whose policy permits direct export.                               |
| **Agent → human**         | An agent instance requests access; the export completes only after a human approves the pending activity.                                              |
| **Multi-agent consensus** | Instances of separate agent-role users must each sign the same export before the secret is released, and only the designated recipient can decrypt it. |

## Key implementation decisions

| Decision                   | What to consider                                                                                                                                                                                    | Learn more                                    |
| :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- |
| **Secret classification**  | Bind static properties at import time (`kind`, `environment`, `requiresConsensus`, ...) so policies target classes of secrets instead of individual IDs.                                            | [Secret Storage](/features/secrets)           |
| **Agent identity**         | Model each agent type or role as a durable Turnkey user. A scoped, expiring session key authenticates each ephemeral instance of that user, making credential delegation easy to express in policy. | [Sessions](/features/authentication/sessions) |
| **Consensus requirements** | Decide which secret classes need one agent role, several, or a specific combination of user tags (e.g. one `browser-agent` *and* one `payment-agent`).                                              | [Policy Engine](/features/policies/overview)  |
| **Recipient control**      | The export payload is encrypted to a single ephemeral public key. Decide which party generates that key. That party, and only that party, can read the secret.                                      |                                               |
| **Revocation**             | Invalidate a session key to revoke one agent instance, or delete the agent-role user to revoke every instance of that role.                                                                         | [Sessions](/features/authentication/sessions) |

## Example: multi-agent consensus for payments

Model a browser agent and a payment agent as durable Turnkey users, with session keys that authenticate their ephemeral instances. Policy requires both agent roles to approve before the enclave exports card details, and only the payment agent instance holds the decryption key. Neither instance can act alone, and the browser agent never sees the card.

| Need                                             | How Turnkey solves it                                                                                                                              |
| :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| No single agent instance can exfiltrate the card | Consensus policy requires approvals from both durable agent-role users before the enclave re-encrypts the secret                                   |
| Approver ≠ recipient                             | The payload is encrypted to the payment agent instance's ephemeral key; the browser agent instance's approval releases a ciphertext it cannot read |
| Agent instances act in parallel                  | Both instances sign and submit the byte-identical export request in any order. Turnkey matches them to the same activity                           |
| Every access is attributable                     | Each export and approval is signed with a session key and logged under the durable agent-role user                                                 |

### Policy: require two agent roles for credit card access

```json theme={"system"}
{
  "policyName": "Require two agent roles for credit card access",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(u, u.tags.contains('browser-agent')) && approvers.any(u, u.tags.contains('payment-agent'))",
  "condition": "activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS' && secret.static_properties['requiresConsensus'] == 'true' && secret.static_properties['kind'] == 'creditCard'"
}
```

### Implementation steps

<Steps>
  <Step title="Import the credential">
    Import the card once, with static properties that the policy above targets. Any client with import permission can do this. Here, a backend service imports it:

    ```typescript theme={"system"}
    const secretId = await turnkey.apiClient().importSecret({
      plaintext: JSON.stringify({ number: "4242...", exp: "11/29", cvv: "123" }),
      name: "corporateVisa",
      staticProperties: {
        kind: "creditCard",
        requiresConsensus: "true",
      },
    });
    ```
  </Step>

  <Step title="Payment agent instance creates the export proposal">
    The payment agent instance, the intended recipient, generates an ephemeral keypair and builds the proposal. `createExportSecretsProposal` is a local call: it produces the canonical request body and its fingerprint, with no network round trip.

    ```typescript theme={"system"}
    import { generateP256KeyPair } from "@turnkey/crypto";

    const { publicKey, privateKey } = generateP256KeyPair();

    const proposal = paymentAgent.createExportSecretsProposal({
      secrets: [{ secretId }],
      targetPublicKey: publicKey,
      organizationId,
    });
    ```

    The proposal is plain JSON and contains no key material. Share it with co-signing agent instances over any channel.
  </Step>

  <Step title="Both agent instances sign and submit in parallel">
    Each agent instance stamps the identical proposal body with the session key for its durable agent-role user and submits. Order doesn't matter: the first submission creates the activity, and every subsequent identical submission counts as an approval.

    ```typescript theme={"system"}
    await Promise.all([
      paymentAgent.submitExportSecrets(proposal),
      browserAgent.submitExportSecrets(proposal),
    ]);
    ```

    Until the consensus expression is satisfied, the activity reports `ACTIVITY_STATUS_CONSENSUS_NEEDED` and no secret leaves the enclave.
  </Step>

  <Step title="Payment agent instance decrypts">
    Once policy is satisfied, the enclave re-encrypts the card to the payment agent instance's ephemeral key. Only that instance can decrypt:

    ```typescript theme={"system"}
    const [cardJson] = await paymentAgent.awaitExportedSecrets({
      proposal,
      embeddedPrivateKey: privateKey,
    });

    const card = JSON.parse(cardJson);
    ```
  </Step>
</Steps>

<Note>
  For direct human approval instead of a second agent role, skip the co-signing step: the activity stays in `CONSENSUS_NEEDED` until the human approves it from the dashboard or via [approve\_activity](/api-reference/activities/approve-activity).
</Note>

## Next steps

<div style={{display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px'}}>
  <FeatureCard title="Secret Storage" icon="lock-01" href="/features/secrets" description="The encryption model behind imports and exports." />

  <FeatureCard title="Policy Engine" icon="file-shield-02" href="/features/policies/overview" description="Consensus expressions, conditions, and tag-based approvals." />
</div>
