---
id: keys
title: Keys
summary: The two keys, what each may do, where each may go, and what to do first if one gets out.
faces: ["public", "agent"]
section: shared
group: Secure
slug: keys
order: 55
next: ["security-and-csp"]
---
# Keys

There are two, they are not interchangeable, and the difference is not about secrecy alone: it is about what
each one can do.

## The publishable key

Starts `rip_pk_`, then 24 more characters drawn from A–Z, a–z, 0–9, `-` and `_`. Thirty-one in all. It goes
in your page, on the script tag, and anyone who views source can read it. That is the design rather than a
concession.

If you are writing your own check on a key before you send it, match the shape above rather than a narrower
one. Keys are random, so a given key can easily look like it is only digits and lowercase letters without
that being the rule.

It grants no permission at all. Asked what a publishable key may do, the platform answers with every
capability set to false. What it does instead is identify the merchant and the integration, and every route
that accepts it also checks the page's origin against that integration's allowed origins. So a copied key on
somebody else's site does not work: the origin check refuses it before anything else happens.

## The secret key

Starts `rip_sk_`, then 32 more characters from the same set. Thirty-nine in all. It belongs on your server,
read from an environment variable. Never a page, never an app, never a repository, never a log line.

It is shown once, when it is created. There is no route that reads it back, by design: if the one copy is
lost, issue a new one rather than looking for the old.

A secret key carries named permissions, and a route refuses a key that lacks the one it needs rather than
half-working. The three most integrations need:

| permission | what it is for |
|---|---|
| `create_payments` | creating checkout sessions from your server |
| `read_transactions` | reading sessions and payments |
| `manage_webhooks` | registering webhook endpoints and reading deliveries |

Give a key the permissions its job needs and no more. A key that can only read transactions cannot take a
payment even if it leaks.

## Test and live

A key allows test mode, live mode, or both, and that is a property of the key rather than of the request.
A live-only key used while the effective mode is test is refused with `key_live_only`, and a browser meets
that as an unavailable checkout rather than as an error it can fix.

## If a key gets out

**Revoke first. Investigate afterwards.** In that order, and the order is the whole advice.

The instinct under pressure is to work out how bad it is before doing anything: which key, whose commit, what
it could reach, whether it was ever used. Every minute of that is a minute the key still works. Revoking is
one action, it is immediate, and it costs you a redeploy with a new key — which you will be doing anyway, and
which you can do calmly once nothing is exposed.

A revoked key answers `key_revoked` at once, so anything still using it fails loudly and immediately and you
find your own forgotten deployments in minutes. A key quietly rotated while the old one still works fails
slowly and silently: the old key keeps paying, and you learn where it was used only when something breaks
weeks later, or never.

This applies to a publishable key too, though less urgently. It grants nothing on its own, but a key in
someone else's page is a signal worth acting on rather than explaining away.
