# Authentication and API keys

> How company API keys authenticate requests, what each ability allows, and how to create, replace and revoke keys.

- Canonical URL: https://couplrfsm.com/developers/authentication
- Markdown: https://couplrfsm.com/developers/authentication.md
- API version: v1 (base URL https://couplrfsm.com/api/v1)
- OpenAPI specification: https://couplrfsm.com/developers/openapi.yaml
- All documentation pages: https://couplrfsm.com/llms.txt

> **Flip:** An API key is how I know which company a request is for and what it's allowed to do. Give each app its own key with only the abilities it needs. If one leaks, you can turn off just that one.

## How requests are authenticated

Every request to `/api/v1` carries a company API key in one of two headers:

```bash
curl "https://couplrfsm.com/api/v1/me" -H "Authorization: Bearer YOUR_API_KEY"
```

```bash
curl "https://couplrfsm.com/api/v1/me" -H "X-Api-Key: YOUR_API_KEY"
```

Use `Authorization: Bearer` unless the tool you are using cannot set it. Never put a key in a URL.

A key belongs to one company. It can only see and change that company's records. An id that belongs to another company returns `404`, the same as an id that does not exist.

## Abilities

A key carries a list of abilities chosen when it is created. An endpoint that needs an ability the key lacks returns `403` with code `ability_missing`.

| Ability | Allows |
| --- | --- |
| `customers:read` | List and get customers. |
| `customers:write` | Create and update customers. |
| `customers:write` also | Add an address to a customer. |
| `jobs:read` | List and get jobs. Read the [lookup lists](https://couplrfsm.com/developers/api-reference/lookup-lists): job types, technicians, tags, lead sources and market segments. |
| `jobs:write` | Create and update jobs. |
| `jobs:complete` | Mark a job as completed. The customer gets the company's usual messages for a finished job. |
| `invoices:read` | List and get invoices. |
| `invoices:write` | Create draft invoices. A draft is not sent or charged. |
| `invoices:send` | Send a draft invoice to the customer with Couplr's payment link. |
| `payments:read` | List the payments collected on invoices, and what has been refunded. |
| `price-book:read` | List and get price book items. |
| `price-book:write` | Create and update price book items. |
| `estimates:read` | List and get estimates. |
| `estimates:write` | Create an estimate and email it to the customer. |
| `timesheets:read` | List time entries and schedule events. Hours only, no pay. |
| `notes:read` | List the notes on a customer or a job. |
| `notes:write` | Add a note to a customer or a job. |
| `photos:read` | Get time-limited links to a job's and a customer's photos, and to a customer's documents. |
| `equipment:read` | List and get the equipment installed at customers' addresses. |
| `availability:read` | List the open time slots for a location. |
| `bookings:write` | Book a visit for a customer into an open slot. |
| `plans:read` | List and get maintenance plans and the customers enrolled in them. |
| `plans:write` | Enroll a customer in a maintenance plan. The customer completes it on Couplr's checkout page. |
| `leads:read` | List and get leads. |
| `leads:write` | Add a lead. Can start a text conversation with the lead when the request asks for one and states the person's consent. |
| `webhooks:manage` | List, create, change and delete outbound webhook subscriptions. |

`GET /api/v1/me` needs no ability. Give each key the fewest abilities its app needs.

> **Flip:** `leads:write` can make me text someone, so I only do it when a request sets both `start_conversation` and `sms_consent`. Leave them out and I just add the lead for the office to call.

> **Flip:** Go easy with `webhooks:manage`. A key that has it can tell me to send your events to any address it likes. Only give it to an app that needs to set up its own deliveries.

## Create a key

Only a company admin can manage keys.

1. Open **Integrations** in the Couplr web app (`https://couplrfsm.com/dashboard/integrations`) or the Integrations screen in the mobile app.
2. Create a key, name it after the app that will use it, and check its abilities.
3. Choose when it expires: never (the default), in 30 days, 90 days or a year, or on a date you pick.
4. Copy the key when it is shown. It is shown once.

Keys start with `hvp_`. The Integrations page lists each key by name with its first characters, its abilities, when it was last used and when it expires. A key past its end date shows "Expired" and returns `401` with code `api_key_expired`.

## Replace or revoke a key

> **Flip:** Rotate a key to swap it out. You get a new one, and the old one keeps working long enough for you to switch.

To replace a key without downtime, press **Rotate** next to it on the Integrations page:

1. Choose what happens to the old key. By default it keeps working for a short overlap (24 hours unless Couplr support has changed it), then expires by itself. If the old key already had an end date that comes sooner, it stops then instead. If the key may have leaked, choose to turn it off now instead.
2. Choose when the new key expires, if ever.
3. Copy the new key. It has the same name and abilities as the old one, and it is shown once.
4. Switch your app to the new key before the overlap ends.

Revoking takes effect on the next request. A revoked key returns `401` with code `api_key_invalid` and cannot be restored. Revoke a key at once if it may have leaked.

Revoking a key also turns off the webhook subscriptions that key created through the API, so a leaked key can't keep receiving your events. They show "Creating API key revoked" on the Integrations page. A subscription whose key has expired gets no deliveries either. When you replace a key, have your app create its subscriptions again with the new key. Subscriptions added on the Integrations page are not affected.

The abilities of an existing key cannot be changed. Create a new key instead.

## How a key acts inside Couplr

> **Flip:** When an app uses a key, I treat it as if one of your admins did the work.

Requests run with the permissions of a company admin: the admin who created the key, or another admin of the company if that person is no longer one. Records created through the API show up in Couplr the same way records created by that admin would. If the company has no admin left, requests return `403` with code `no_acting_admin`.

## Authentication errors

| Status | Code | Meaning |
| --- | --- | --- |
| `401` | `api_key_missing` | No key was sent, or it does not start with `hvp_`. |
| `401` | `api_key_invalid` | The key is unknown or has been revoked. |
| `401` | `api_key_expired` | The key's end date has passed, or it was rotated and its overlap has ended. |
| `401` | `sandbox_expired` | The key belongs to a private trial sandbox whose access window has ended. |
| `403` | `ability_missing` | The key does not have the ability this endpoint needs. |
| `403` | `no_acting_admin` | The company has no admin user for the key to act as. |

Requests without a valid key are limited to 60 a minute per IP address. See [Rate limits](https://couplrfsm.com/developers/errors-and-limits#rate-limits).
