Credential vault

Manage vault credentials directly from the Browser Agent API — the same vault the Console manages, scoped to your account. Store a login once, then reference it on runs with credential_id. Secret material is write-only: no endpoint ever returns a password or TOTP seed, in any response, error, or log.

Create

POST /api/v1/credentials — requires domain, username, password. Optional: totp_secret (base32 seed — OTP challenges are then answered in-process at run time), custody (defaults to trusted_runners — see Custody policies), mfa, source_id. Username, password, and TOTP seed are envelope-encrypted at rest (per-secret AES-256-GCM data key wrapped by Cloud KMS, automatic rotation); the response returns id + metadata only.

Create — field reference

POST/api/v1/credentials (createCredential)

Create a vault credential

Request body

FieldTypeReqDescription
domainstringyesLogin site domain, e.g. portal.example.com (scheme/path stripped).
usernamestringyes
passwordstringyesStored envelope-encrypted: per-secret AES-256-GCM data key wrapped by Cloud KMS (automatic key rotation). Never returned by any endpoint.
totp_secretstringOptional base32 TOTP seed — OTP challenges are answered in-process at run time. Sets mfa: totp.
custodystring
default "trusted_runners"
Custody policy: any | trusted_runners | deck_only | pinned:<runner>[,<runner>…]. See /docs/security/custody.
mfastring
none | totp | sms-relay | passkey
MFA mode; inferred as totp when totp_secret is set.
source_idstringOptional catalog source id when the site comes from the sources list.

Response

FieldTypeReqDescription
idstringcred_… — pass as credential_id on POST /v1/responses
object"banana_peel.credential"
domainstring
source_idstring
username_maskedstringMasked username for display (e.g. jdo***@example.com).
mfastring
none | totp | sms-relay | passkey
custodystringany | trusted_runners | deck_only | pinned:<runner>[,<runner>…]
healthstring
ok | stale | failed
created_atstring (date-time)
last_usedstring (date-time)

HTTP statuses

  • 201 — Created — id + metadata only (never the secret)
  • 400 — Invalid request (`invalid_credential` for missing/oversized fields, `invalid_custody` for a malformed policy)
  • 401 — Invalid API key

Generated from OpenAPI. Do not hand-maintain this table.

curl -s "$BANANA_PEEL_BASE/api/v1/credentials" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "portal.example-utility.com",
    "username": "jdoe@example.com",
    "password": "hunter2-rotate-me",
    "totp_secret": "JBSWY3DPEHPK3PXP",
    "custody": "trusted_runners"
  }'
# → 201
# {
#   "id": "cred_9f2ab41c77d0e83b4a51",
#   "object": "banana_peel.credential",
#   "domain": "portal.example-utility.com",
#   "source_id": null,
#   "username_masked": "jdo***@example.com",
#   "mfa": "totp",
#   "custody": "trusted_runners",
#   "health": "ok",
#   "created_at": "2026-08-13T14:00:00.000Z",
#   "last_used": null
# }

List / fetch

GET /api/v1/credentials lists your non-revoked credentials; GET /api/v1/credentials/:id fetches one. Both return metadata only: id, domain, masked username, mfa, custody, health, created_at, last_used.

Credential object

Vault credential metadata. Secret material (password / TOTP seed) is write-only and never appears here.

FieldTypeReqDescription
idstringcred_… — pass as credential_id on POST /v1/responses
object"banana_peel.credential"
domainstring
source_idstring
username_maskedstringMasked username for display (e.g. jdo***@example.com).
mfastring
none | totp | sms-relay | passkey
custodystringany | trusted_runners | deck_only | pinned:<runner>[,<runner>…]
healthstring
ok | stale | failed
created_atstring (date-time)
last_usedstring (date-time)

Generated from OpenAPI. Do not hand-maintain this table.

curl -s "$BANANA_PEEL_BASE/api/v1/credentials" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY"
# → { "object": "list", "data": [ /* metadata rows — never secrets */ ] }

Use on a run

Pass the id as credential_id on POST /api/v1/responses. After custody is proven satisfiable, Banana Peel decrypts the secret and injects username / password (and optional TOTP) into the task the same way inline credentials would — onto whichever runner routing selects (smart, steel, browserbase, deck, …). Vault usage never pins routing to deck; when routing doesselect Deck (pinned, smart, race, or learning), the credential is additionally attached through Deck's native vault — registered on the run's source and reused across runs — because Deck gates login-walled sources on an attached credential rather than the instruction text. deck_only remains an opt-in custody policy; the create default is trusted_runners. If the vault credential (or request totp_secret) includes a base32 TOTP seed, OTP asks are answered in-process and the run does not pause. Username/password alone is not enough — the run still pauses for the code. When a target site asks for MFA, 2FA, or an OTP, the run pauses immediately and asks the user for the code. It does not keep trying other runners. See MFA / OTP and Custody policies.

Secrets ride the dispatch only — the persisted run record, the execution trace, and every API/webhook payload keep the pre-injection task text. If custody rules out every runner in the requested routing, the run fails free with 409 custody_unsatisfiable before anything dispatches or bills.

curl -s "$BANANA_PEEL_BASE/api/v1/responses" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "banana-peel",
    "input": "Download the latest bill PDF from https://portal.example-utility.com",
    "credential_id": "cred_9f2ab41c77d0e83b4a51",
    "routing": "smart",
    "background": true
  }'

Update

PATCH /api/v1/credentials/:id — change the custody policy, rotate the password, or set / replace / remove the TOTP seed (totp_secret: null removes it).

Update — field reference

PATCH/api/v1/credentials/{id} (updateCredential)

Update custody policy or rotate secrets

Parameters

FieldTypeReqDescription
idpath: stringyes

Request body

FieldTypeReqDescription
custodystringany | trusted_runners | deck_only | pinned:<runner>[,<runner>…]
passwordstringRotate the stored password.
totp_secretstring | nullReplace the TOTP secret, or null to remove it.
mfastring
none | totp | sms-relay | passkey

Response

FieldTypeReqDescription
idstringcred_… — pass as credential_id on POST /v1/responses
object"banana_peel.credential"
domainstring
source_idstring
username_maskedstringMasked username for display (e.g. jdo***@example.com).
mfastring
none | totp | sms-relay | passkey
custodystringany | trusted_runners | deck_only | pinned:<runner>[,<runner>…]
healthstring
ok | stale | failed
created_atstring (date-time)
last_usedstring (date-time)

HTTP statuses

  • 200 — Updated credential metadata
  • 400 — Invalid field (`invalid_credential` / `invalid_custody`)
  • 404 — Not found (`credential_not_found`) — unknown, revoked, or owned by another account

Generated from OpenAPI. Do not hand-maintain this table.

curl -s -X PATCH "$BANANA_PEEL_BASE/api/v1/credentials/cred_9f2ab41c77d0e83b4a51" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custody": "pinned:browserbase,steel" }'

Revoke

DELETE /api/v1/credentials/:id soft-deletes the credential: it disappears from lists and can no longer be used on runs.

curl -s -X DELETE "$BANANA_PEEL_BASE/api/v1/credentials/cred_9f2ab41c77d0e83b4a51" \
  -H "Authorization: Bearer $BANANA_PEEL_API_KEY"
# → { "id": "cred_9f2ab41c77d0e83b4a51", "object": "banana_peel.credential", "deleted": true }

Scoping & errors

  • Credentials are scoped to the API key's account — exactly the set you see in Console → Vault. A credential owned by another account is indistinguishable from a nonexistent one: always 404 credential_not_found, never a 403.
  • 400 invalid_credential — missing required fields, wrong types, or oversized values (domain ≤ 253, username ≤ 320, password ≤ 1024, totp_secret ≤ 128 characters). The offending value is never echoed back.
  • 400 invalid_custody — malformed custody policy. Valid: any | trusted_runners | deck_only | pinned:<runner>[,<runner>…].
  • 401 invalid_api_key — pass Authorization: Bearer bp_live_… or x-api-key.

Command Palette

Search for a command to run...