# API Key Authentication

Generate and use API keys for direct company access.

## Overview

API keys provide a simple authentication method for merchants to integrate their own systems with Cheqi. Each key represents exactly one company. It can access that company's direct store locations, but it cannot act for a parent, child, sibling, or unrelated company.

**Key Benefits:**

- **Simple to use** - No token management or OAuth flows required
- **Direct integration** - Perfect for companies issuing receipts through their own systems
- **Multi-store access** - Manage all stores that belong directly to the key's company
- **No external access** - Cannot perform actions for other organizations


**Perfect for:**

- Merchants who own their POS system and want direct integration
- E-commerce businesses managing their own webshop platform
- **Retail chains** whose locations all use the same legal company
- Companies that need straightforward authentication without OAuth complexity


For a corporate group or franchise with several Cheqi companies, use one API key per legal company or use [OAuth multi-company authorization](/authentication/oauth#multiple-companies).

Merchant Ownership Required
API keys are only available for merchants who own and control their point-of-sale system or e-commerce platform. If you're using a third-party POS provider or webshop platform, you'll need to use [OAuth authentication](/authentication/oauth) instead.

Exact Company Context
An API key never inherits receipt authority from a parent-company relationship. To issue for a child company, authenticate as that child company.

Keep API Keys Secret
An API key can do everything its scopes allow for the authenticated company and
its directly owned stores. Never commit it to version control or expose it in a
client application.

## Generating an API Key

### Via the Cheqi Mobile App

1. Open the **Cheqi app** on your device
2. Navigate to **Profile** → **Company Profile**
3. Select **Developer Tools** → **API Keys**
4. Tap **Create New API Key**
5. Enter a **descriptive name** (e.g., "Production POS System")
6. Select the **scopes** the integration needs; at least one is required. See
[API key scopes](#api-key-scopes).
7. **Copy the key immediately** - it will only be shown once


One-Time Display
The API key is displayed only once during creation. Store it securely in your application's configuration.

### Key Format

API keys follow this format:

```
sk_1234567890abcdef1234567890abcdef
```

- **Prefix:** `sk_` (Cheqi Secret Key)
- **Key:** 32-character alphanumeric string


## Using API Keys

### Authentication Header

Include your API key in the `Authorization` header with the `Bearer` scheme:

```bash
curl https://api.cheqi.io/recipient/resolve \
  -H "Authorization: Bearer sk_1234567890abcdef1234567890abcdef" \
  -H "Content-Type: application/json" \
  -d '{...}'
```

### SDK Configuration

Configure your SDK with the API key:

**HTTP Example:**

```bash
# Direct API integrations use the split flow:
# 1. POST /recipient/resolve
# 2. Encrypt the definitive receipt payload locally for every returned device
# 3. POST /receipt/encrypted, or use the explicitly selected fallback route
curl https://api.cheqi.io/recipient/resolve \
  -H "Authorization: Bearer sk_1234567890abcdef1234567890abcdef" \
  -H "Content-Type: application/json" \
  -d '{...}'
```

**Conceptual SDK Usage:**

```java
// Initialize SDK with API key
CheqiSDK sdk = CheqiSDK.builder()
    .apiEndpoint(Environment.PRODUCTION)
    .apiKey("sk_1234567890abcdef1234567890abcdef")
    .build();

// Use SDK to send receipts
ReceiptResult result = sdk.getReceiptService()
    .issueReceipt(identificationDetails, receiptPayload);
```

See the [Java](/sdk/java), [.NET](/sdk/dotnet), [JavaScript](/sdk/javascript), and [Python](/sdk/python) SDK guides for implementation details. For another language, see [SDK Availability](/sdk/overview).

### Secure Storage

Never Hardcode API Keys
**Never hardcode API keys** in your source code or commit them to version control. Always use secure configuration management.

**Recommended approaches:**

- Environment variables
- Secure configuration files (excluded from version control)
- Secret management services (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault)
- Encrypted configuration stores


## API Key Scopes

Every API key has the scopes you select when you create it. A key can only do
what its scopes allow, and only for **its exact company context**. Give each
key only the scopes its integration needs, and use separate keys for separate
integrations.

API keys use the same scopes as [OAuth client applications](/authentication/oauth):

| Permission in the Cheqi app | Scope | Allows |
|  --- | --- | --- |
| Issue Receipts | `receipt:issue` | Send receipts as the company, optionally for one of its stores |
| View Stores | `store:read` | Read the company's stores and locations |
| Manage Stores | `store:manage` | Create and manage the company's stores and locations |
| View Company | `company:read` | Read the company profile and authorization metadata |
| Receive Receipts Automatically | `receipt:read` | Receive receipts that Cheqi delivers to the company automatically |
| Receive User-shared Receipts | `receipt:read:user-shared` | Receive receipts that company members submit to the company |
| Invite Company Members | `company:members:invite` | [Invite people](/companies/inviting-employees) to join the company |


No scope lets a key act for a parent, child, sibling or unrelated company.

### Common scope sets

| Integration | Scopes |
|  --- | --- |
| **Sending receipts** from a POS or webshop | `receipt:issue`, `store:read`, `store:manage`, `company:read` |
| **Receiving receipts** in an expense or accounting system, including employee submissions | `receipt:read`, `receipt:read:user-shared`, `company:read`, `company:members:invite` |


Use one key for sending and a separate key for receiving. The
[Odoo connector](/odoo/overview) works this way: its Point of Sale and
webshop use the sending key, and [Cheqi Expenses](/odoo/expenses) uses the
receiving key.

Company and Store Boundary
The company in the API key is always the legal receipt issuer. A `storeId` adds the physical or operational location; it never changes the issuer.

### Multi-Store Example

If one retail company operates several locations, create those locations as
stores under that company and use the company's API key during encrypted
submission:

```json
{
  "matchId": "match_opaque",
  "storeId": "amsterdam-store-uuid",
  "deviceDeliveries": [
    {
      "deviceRecipientId": "rcpt_temporary",
      "encryptedContent": "base64-ciphertext",
      "encryptedAesKey": "base64-wrapped-key"
    }
  ]
}
```

This succeeds only when `amsterdam-store-uuid` belongs directly to the company
represented by the API key. Using a parent-company key with a child company's
store returns `403 Forbidden`. The deprecated `childCompanyId` field cannot
change the legal issuer.

See [Companies, Child Companies, and Stores](/companies/structure) for corporate-group and franchise examples.

## Managing API Keys

### Listing Active Keys

View all API keys for your company in the Cheqi app:

**Profile** → **Company Profile** → **Developer Tools** → **API Keys**

Each key shows:

- **Name** - Descriptive label you provided
- **Key Prefix** - First 8 characters (e.g., `sk_12345...`)
- **Created** - When the key was generated
- **Last Used** - Most recent API call timestamp
- **Status** - Active or Revoked


### Revoking API Keys

Revoke a key immediately if:

- ❌ The key was accidentally exposed
- ❌ An employee with access left the company
- ❌ You're rotating keys for security
- ❌ The integration is no longer in use


**To revoke:**

1. Go to **API Keys** in the app
2. Select the key to revoke
3. Tap **Revoke Key**
4. Confirm the action


Irreversible Action
Revoking a key is immediate and irreversible. All API calls using that key will fail with `401 Unauthorized`.

## Best Practices

### Security

#### Never Hardcode API Keys

```java
// ❌ BAD - Hardcoded key
CheqiSDK sdk = CheqiSDK.builder()
    .apiKey("sk_1234567890abcdef1234567890abcdef")
    .build();

// ✅ GOOD - Environment variable
CheqiSDK sdk = CheqiSDK.builder()
    .apiKey(System.getenv("CHEQI_API_KEY"))
    .build();
```

#### Use Different Keys for Different Environments

```bash
# Development
CHEQI_API_KEY=sk_dev1234567890abcdef

# Staging
CHEQI_API_KEY=sk_staging1234567890abcdef

# Production
CHEQI_API_KEY=sk_prod1234567890abcdef
```

#### Rotate Keys Regularly

1. Generate a new API key
2. Update your application configuration
3. Deploy the update
4. Verify the new key works
5. Revoke the old key


#### Monitor Key Usage

Regularly check the **Last Used** timestamp in the app to detect:

- Unused keys that should be revoked
- Unexpected usage patterns
- Keys that may have been compromised


### Key Naming

Use descriptive names that indicate:

- **Environment:** Production, Staging, Development
- **System:** POS System, Webshop, Mobile App
- **Location:** Store 1, Store 2, Warehouse


**Examples:**

- `Production POS - Store Amsterdam`
- `Production POS - Northstar Retail NL B.V.`
- `Development Testing Key`


## Error Handling

### Common Errors

| Status Code | Error | Solution |
|  --- | --- | --- |
| `401` | Invalid API key | Verify the key is correct and not revoked |
| `401` | API key revoked | Generate a new key and update your configuration |
| `403` | Insufficient permissions | API keys have full company access - check company ownership |
| `429` | Rate limit exceeded | Implement exponential backoff and retry logic |


### Example Error Response

```json
{
  "error": "unauthorized",
  "message": "Invalid or revoked API key",
  "timestamp": "2024-01-13T21:00:00Z"
}
```

## Testing

### Sandbox Environment

Use the [Cheqi Sandbox](/sandbox/getting-started) to test your API key integration. Create an account at [sandbox.portal.cheqi.io](https://sandbox.portal.cheqi.io), register a company, and generate an API key.

```bash
# Sandbox environment
CHEQI_API_BASE_URL=https://sandbox.api.cheqi.io
CHEQI_API_KEY=sk_your_sandbox_key
```

**SDK Configuration:**

```java
CheqiSDK sdk = CheqiSDK.builder()
    .customApiEndpoint("https://sandbox.api.cheqi.io")
    .apiKey(System.getenv("CHEQI_API_KEY"))
    .build();
```

The sandbox has pre-configured test customers with known card PARs, so you can test the full receipt flow end-to-end.

### Verify API Key

Test your API key with a simple request:

```bash
curl https://sandbox.api.cheqi.io/company/{companyId}/public \
  -H "Authorization: Bearer sk_your_sandbox_key"
```

Expected response:

```json
{
  "id": "uuid",
  "companyName": "Your Company Name",
  "companyEmail": "info@yourcompany.com"
}
```

## Next Steps

- [Inviting Employees](/companies/inviting-employees) - Invite people to the company represented by your API key
- [Delivery Options](/receipts/overview) - Choose app delivery or a web wallet link
- [Java SDK](/sdk/java) - Java SDK implementation guide
- [.NET SDK](/sdk/dotnet) - .NET implementation guide
- [JavaScript SDK](/sdk/javascript) - JavaScript and TypeScript implementation guide
- [Python SDK](/sdk/python) - Python implementation guide
- [SDK Availability](/sdk/overview) - Request another SDK language