Customer API authentication
Request, use, protect, and rotate a Steer Phones API key, and understand what its scopes allow.
Every approved customer API request carries a secret API key in the X-API-Key header. The key
identifies a grant — the phone systems it may read — and one set of permissions. It is not a user
login, and it does not inherit whatever the person who requested it can do in the dashboard.
Understanding that boundary is most of what you need to integrate safely.
Anatomy of a key
A secret key looks like sk_phones_<short>_<secret>. The sk_phones_ prefix marks it as a secret
server-side credential.
- One grant target. A key is issued for exactly one target: a single phone system, a phone system group, or an organization. The phone systems the key covers are resolved from that target live on every request — a system that joins the group tonight is readable with the same key tomorrow, and one that leaves stops being readable, with no reissue either way. An organization key covers the organization’s directly-attached systems only, never those of a child organization. A phone system outside the grant answers with the same not-found response as a system that does not exist.
- Fixed permissions. A key carries the scopes granted when it was issued (or a role Steer Phones bound it to). Nothing about the key escalates at runtime.
- Shown once. Only the hashed key is stored. After issuance, Steer Phones can show you the name, the prefix, and the last four characters — never the key itself. If it is lost, it is replaced, not recovered.
- Recorded on every use. The key’s last-used time and source address are kept, and every authenticated request is written to your activity records.
Send the key in the X-API-Key header. An Authorization: Bearer sk_phones_… header is also
accepted for clients that can only set a bearer token.
Host: api.steerphones.com
X-API-Key: sk_phones_…
Accept: application/json
A separate key type exists for dynamic number insertion: publishable keys, prefixed pk_phones_,
which are safe to place in the tracking snippet on a public web page. Publishable keys are rejected
by the customer API — they can only reach the number-tracking service. Never treat the two as
interchangeable, and never put an sk_phones_ key in a browser.
Request a key
Keys are provisioned by Steer Phones. There is no self-service page in the dashboard that creates one, so the flow is a conversation with your Steer Phones contact rather than a button.
Have this ready when you ask:
- Which grant target the integration acts on — one phone system, one phone system group, or one organization. A group or organization key covers the target’s member systems as they stand at each request, so a fleet-wide integration usually needs exactly one key.
- What the integration does, in one sentence — this drives which scopes are granted.
- The narrowest scopes that do the job. Ask for read where you only read — and remember that
playing voicemail audio needs
recordings:readalongsidevoicemails:read(see below). - Source addresses, if your integration calls from fixed addresses. A key can be restricted to an allowlist so it is unusable from anywhere else.
- An expiry date, if the integration is temporary. A key can be issued to expire automatically.
- The rate tier. A key covering several systems shares one request budget across all of them,
so ask for the
premiumtier on a group or organization key. See API versioning and errors.
Steer Phones returns the key once, over a channel you agree on. Put it straight into your secret store.
See what a key covers
GET /v1/customer/phone-systems lists the phone systems the key’s grant covers at this moment. It
requires no scope — any valid customer key can call it — which makes it the right first call for
two jobs: proving a freshly deployed key works at all, and discovering the phone system identifiers
the resource operations need in their paths. Because coverage is resolved live, calling it again
after a membership change shows the new coverage without any change to the key. The walkthrough is
in Use the customer API operations.
What scopes allow
Scopes follow a resource:action shape — calls:read, recordings:read. Two rules matter when
you ask for them:
- Write includes read. In the scope model, granting a
writescope also grants itsread, so you never need both. At version 1 this stays theoretical: the published operations are all reads, and the scopes issued on a customer key are the read scopes below. - Least privilege is enforceable. A request outside a key’s scopes is refused with a 403, whether or not the operation exists.
The scopes issuable on a customer key today — matching the published version 1 operations:
| Scope | Published operations it unlocks |
|---|---|
calls:read |
Call-log list and detail (with transcript); call-analysis list and detail; the two bulk call-reporting operations, which additionally need the grant to cover the whole phone system |
recordings:read |
Recording list and detail; the signed audio URL operation |
voicemails:read |
Voicemail list and detail (with transcript) |
phones:read |
Phone inventory and hardware detail |
phone-numbers:read |
Phone-number inventory and detail |
extensions:read |
Extension inventory, assignments, and unanswered routing |
agent-configs:read |
Call Assistant routing identities |
call-groups:read |
Call-group inventory, membership, and unanswered routing |
call-menus:read |
Call-menu inventory, choices, prompts, and fallback routing |
One scope, two levels of authority: the bulk call-reporting operations need calls:read across
the whole phone system. A key whose calls:read is narrowed to one person’s own calls can read
those calls through the call-log operations, but both reporting operations refuse it with 403 before
any reporting data is read — extracting a phone system’s whole call history is a different
authority from reading your own calls. This is not a separate scope to request; it is how broadly
the calls:read on the key was granted, so say that the integration reports on the whole phone
system when you ask for the key. Reporting also returns no recording audio and no signed media URL,
so calls:read never becomes a route to audio. The operations are described in
Use the customer API operations.
One thing that surprises people: tenant-wide calls:read shows you the names of the call groups
and extensions your calls actually touched, without call-groups:read or extensions:read. A
report row names the call group that took the call and the extension that answered it, and the
dimensions snapshot resolves those same names when they change. That is a deliberately narrow
window — it covers only what retained calls reference, and only the display values a report needs.
The inventory operations behind call-groups:read and extensions:read are still the way to list
what exists, including the groups and extensions no call ever reached, along with their membership
and routing configuration.
One pairing to plan for: voicemail audio needs recordings:read in addition to
voicemails:read. The voicemail operations return metadata and transcripts only; the audio
itself is fetched through the recordings signed-URL operation, which sits behind recordings:read.
An integration that lists voicemails and plays them therefore asks for both scopes.
Topology scopes are independent. A typed routing reference does not grant access to its target:
following it still requires the target resource’s scope and a grant that covers its
phoneSystemId. Ask only for the resource scopes the integration follows.
Keys issued before the topology operations were published keep their original scopes and cannot reach the new routes. Steer Phones must reissue a key with the needed topology scopes; an existing key is never widened in place.
Other resource areas — messages, contacts, campaigns, DNI, notifications, webhooks, recording exemptions, diagnostics, and users — become grantable as their operations pass the publication gate and appear in the reference. Until an area’s operations are published, its scope is not issued on a customer key, so absence from the table above means “not yet”, not “never”.
Scopes that reach across tenants are reserved for Steer Phones’ own operations and are never granted on a customer key. Neither can a key manage API keys — key lifecycle is never available to a key, only to an authorized person.
Keep a key safe
- Store it in a server-side secret manager and read it from configuration at runtime.
- Never place it in browser code, a mobile app, a URL query string, a support ticket, a screenshot, a spreadsheet, or source control.
- Send it only over HTTPS to
https://api.steerphones.com. Do not send it to any other host, including a documentation site. - Give each integration its own key. Shared keys make revocation an outage for everyone.
- Restrict the key to your integration’s source addresses when they are stable.
Treat a key that appeared anywhere unexpected as compromised and ask Steer Phones to revoke it. Revocation takes effect immediately.
Rotate a key
Rotation issues a replacement and gives the old key a short overlap period before it expires, so you can deploy the new value without a gap in service. Ask Steer Phones to roll the key, deploy the new value during the overlap, then confirm the old key stopped being used.
Rotate on a schedule for long-lived integrations, and immediately whenever someone with access to the key leaves, a laptop or CI system holding it is lost, or the value was pasted somewhere it should not have been. An expired or revoked key fails authentication outright rather than silently degrading.
See what a key did
Every authenticated request — allowed or denied — is recorded. In the dashboard, open Records > Activity and filter by API key to see what an integration reached, when, from which address, and whether it was permitted. Selecting a key opens its own history, including activity from the keys it replaced during rotation, so a rotation does not break the trail.
This is the first place to look when an integration behaves unexpectedly: a run of denied rows usually means a scope was not granted, and a gap usually means the integration stopped calling rather than the key failing.
Expected result
Your integration reads its key from a secret store, sends it in X-API-Key over HTTPS, and receives
either data or a clear typed error. Nobody outside the integration ever sees the key value, and the
activity records show exactly what it touched.
Troubleshooting
- 401 with an authentication code: The key is missing, mistyped, expired, or revoked. Confirm the header name and that the value has no whitespace or line breaks.
- 404 for a phone system you expected to read: The system is not covered by the key’s grant —
it is outside the group or organization the key was issued for, or the key was issued for a
different system. A non-covered system is deliberately indistinguishable from a nonexistent one,
so start by listing what the key actually covers with
GET /v1/customer/phone-systems. - 403 naming a missing scope: The key was not granted that permission. Ask Steer Phones to widen the scopes, or point the integration at what it is allowed to reach.
- 403 on a reporting operation while the call-log operations work: The key holds
calls:read, but narrowed to one person’s own calls rather than across the phone system. Bulk reporting needs the phone-system-wide grant; ask Steer Phones to reissue the key with it. - 403 on a publishable key: A
pk_phones_key was sent to the customer API. Publishable keys work only with number tracking. - Requests fail only from a new server: The key has a source-address allowlist that does not include the new address.
- 429 responses: You are above the request rate for that key. See API versioning and errors.