---
title: "API authentication, keys and scopes"
description: "How QuotWay API keys work: live and test keys, the five scopes, IP allowlists, expiry, rolling a key without downtime, and revoking one."
url: "https://www.quotway.com/docs/api/authentication"
type: "documentation"
category: "api"
updated: "2026-09-30"
locale: "en"
source: "QuotWay - B2B Quote & Negotiation App for Shopify"
---

# API authentication, keys and scopes

**Read time:** 7 minutes.
**Who it's for:** Developers integrating with QuotWay, and the store Admin who issues their keys.

> ⚠️ API keys require the **Enterprise** plan (a live trial counts). If a store later moves to a
> lower plan, its keys are kept but every request returns `402 plan_required` until the store is
> back on Enterprise.

## How do I authenticate?

Every request carries one API key as a bearer token:

```http
GET /v1/quotes HTTP/1.1
Host: api.quotway.com
Authorization: Bearer qw_live_2Xb9…
```

There is no OAuth flow, session or cookie. A key belongs to exactly one store, so the store is
implied by the key - you never pass a shop domain. Keys are for **server-to-server** use; the API
does not send CORS headers, so a browser page can't call it directly (and shouldn't: that would
expose the key).

## What does a key look like?

```
qw_live_<49 characters>
qw_test_<49 characters>
```

- `qw_live_` keys work on any store. `qw_test_` keys are for **Shopify development stores** only
  (see [Test keys](#test-keys)).
- The 49 characters are letters and digits: 43 random characters followed by a 6-character
  checksum. The checksum lets QuotWay (and secret scanners) reject a mistyped or truncated key
  instantly.
- The first 12 characters (for example `qw_live_2Xb9`) are the key's **display prefix** - it's what
  you'll see in the admin, so you can tell keys apart without revealing them.

QuotWay stores only a keyed hash of each key. **The full key is shown once, when it's created or
rolled, and can never be shown again.** If you lose it, roll or revoke it and create another.

## Who can manage keys?

| Role | See the key list | Create, roll or revoke keys |
|---|---|---|
| Admin | Yes | Yes |
| Manager | Yes | No |
| Sales rep | Yes | No |
| View only | No | No |

Key management is Admin-only because a key is machine access to the store's quotes - and, with the
`read_customer_data` scope, to buyer personal data. That's an access decision, not a setting.

Creating a key also requires the Admin to accept the [API Terms of Use](/api-terms) for the store.
QuotWay records who accepted which version, and when; a rolled key carries its predecessor's
acceptance. If you tick **Read buyer personal data**, the form also warns that the key can read
buyers' names, emails, phone numbers and addresses - and that anything it copies into another
system is yours to keep secure and to delete when a buyer asks to be erased.

If the store moves below the Enterprise plan, its keys stop working (`402 plan_required`) but are
**not deleted** - they start working again after an upgrade. The API keys page still lists them,
with **Revoke**, so you can retire any you no longer trust.

## Scopes

Grant each key only the scopes its integration needs. A request to an endpoint whose scope the key
lacks returns `403 insufficient_scope` with the missing scope(s) in `required_scopes`.

| Scope | Label in the admin | Grants |
|---|---|---|
| `read_quotes` | Read quotes | Quotes, lines, totals, per-quote events and the events feed (`GET /v1/quotes`, `/v1/quotes/{id}`, `/v1/quotes/{id}/events`, `/v1/events`) |
| `read_customer_data` | Read buyer personal data | Adds the buyer's name, email, phone, addresses, custom-field answers and notes (the `customer` object and each line's `buyer_note`) to quote responses, allows the `customer_email` filter, and - together with `read_quotes` - unlocks quote PDFs (`GET /v1/documents/{id}`), which contain the same details. Only grant it to systems that need them. |
| `read_analytics` | Read analytics | `GET /v1/analytics/summary` |
| `write_quotes` | Create quotes and post messages | `POST /v1/quotes`, `POST /v1/quotes/{id}/messages`, `POST /v1/quotes/{id}/send-proposal` |
| `manage_webhooks` | Manage webhooks | `GET`/`POST /v1/webhooks`, `GET`/`DELETE /v1/webhooks/{id}` |

`GET /v1/ping` needs no scope - any valid key works - and returns the key's scopes.

`read_customer_data` only widens what `read_quotes` returns (and adds documents); it doesn't grant
access to anything on its own. A key with `write_quotes` but not `read_quotes` can still create a quote - the `201`
response contains the new quote - but can't list or fetch quotes afterwards.

## Test keys

Tick **Test key (qw_test_)** when creating a key on a **Shopify development store** (the option only
appears there). A test key:

- only works on development stores - used against a live store it returns
  `401 test_key_on_live_store`;
- works on the development store's **real QuotWay data**. There is no separate sandbox: a quote you
  create with a test key is a real quote in that store.

The prefix exists so that code, logs and config make it obvious which key is which.

## Expiry

Choose **Never**, **In 30 days**, **In 90 days** or **In 1 year** when you create a key. After the
expiry time the key returns `401 invalid_api_key`. A key within 24 hours of expiring shows
**Expiring soon** in the admin. Expired and revoked keys stay visible in the list for 30 days, then
drop off.

## IP allowlist

Optionally restrict a key to the IP addresses your integration calls from. In **Allowed IP
addresses**, enter up to **20** addresses, one per line. Leave it empty to allow any IP.

- Entries are exact IPv4 or IPv6 addresses. **CIDR ranges aren't supported** - list each egress
  address.
- A request from any other address returns `403 ip_not_allowed`.
- The allowlist is checked after the key itself, so an invalid key still gets `401`.

## Rolling a key (zero-downtime rotation)

Select **Roll** on an active key to replace it without an outage:

1. QuotWay creates a **new key** with the same name, scopes, test flag and IP allowlist, and shows
   it once.
2. The **old key keeps working for 24 hours** (or until its own expiry, if that's sooner), then
   stops.
3. Deploy the new key within that window.

If the old key had an expiry, the new key gets the same lifetime, counted from the moment you roll.

A key can be rolled **once**. To rotate again, roll the replacement.

## Revoking a key

Select **Revoke**, then **Confirm revoke**, to stop a key **immediately**. Every request with it returns
`401 invalid_api_key` from then on. Revoking can't be undone - create a new key if you need access
again. Revoke rather than roll if you think a key has leaked.

## What happens if the store uninstalls QuotWay?

Uninstalling QuotWay **revokes every API key** for the store (and disables its webhook endpoints).
Reinstalling doesn't bring them back - an Admin creates new keys. A key for a store that isn't
actively installed returns `401 invalid_api_key`.

## Limits

- Up to **10 active keys** per store. Revoke one to create another.
- Each key is rate-limited on its own, and all of a store's keys share a store-wide limit - see
  [Rate limits](/docs/api/rate-limits).

## Seeing what a key has done

In **Settings → Integrations → API keys**, the key list shows each key's **Last used** time, and
the **Requests** button opens its recent requests (the last 100): time, method, route, status, duration and the `request_id`.
QuotWay logs the route template (for example `/v1/quotes/{id}`), never request or response bodies.
When you contact support about a failed call, quote the `request_id` from the response body or the
`X-Request-Id` header.

## Security checklist

- Keep keys server-side, in a secrets manager or environment variable. Never ship one in browser or
  mobile code, and never commit one to a repository.
- One key per integration, named after it, so you can revoke one system without breaking the rest.
- Least privilege: leave out `read_customer_data` unless the integration needs buyer contact
  details, and `write_quotes` unless it creates quotes or messages.
- Use an IP allowlist when your integration has fixed egress addresses.
- Roll keys on a schedule and whenever someone with access leaves.

## Related

- [Errors](/docs/api/errors) - `invalid_api_key`, `insufficient_scope`, `ip_not_allowed` and the rest.
- [The quote object](/docs/api/quote-object) - what `read_customer_data` adds.
- [Staff and roles](/docs/staff/staff-and-roles)
