# Managing API Keys

> Source: https://docs.trailspark.ai/docs/api-keys

## Accessing API Keys

Navigate to **Settings** > **API Keys**. Requires Owner, Admin, or Editor role.

## Creating an API Key

Click **Create API Key** and configure:

| Field | Required | Description |
|-------|----------|-------------|
| **Name** | Yes | Descriptive name (e.g., "Production - Marketing Events") |
| **Endpoint Type** | Yes | **Signal Staging** (behavioral signals) or **Product Org Updates** (workspace/account data) |
| **Expiration** | No | Expiration date/time. Leave empty for no expiration |
| **Secret** | No | When enabled, generates a shared secret. Requests must include the secret in the `X-Api-Secret` header |
| **Payload Template** | Conditional | Field mappings -- required for Product Org endpoint type only |

Click **Create API Key** to generate.

> [!WARNING]
> Copy your API key immediately. The full key (format: `sk_...`) is only displayed once and cannot be retrieved later.

## Endpoint Types

### Signal Staging (Default)

For behavioral signals: page views, form submissions, email engagement, product feature usage.

Webhook URL:
```
https://app.trailspark.ai/api/signal-staging/webhook/{apiKey}
```

### Product Org Updates

For product organization/workspace data: plans, user counts, MRR, trial status, feature flags.

Webhook URL:
```
https://app.trailspark.ai/api/product-orgs/webhook/{apiKey}
```

See [Product Org Updates](/docs/product-org-updates) for payload template configuration.

## Using API Keys

### URL Formats

```
# Universal webhook
POST https://app.trailspark.ai/api/signal-staging/webhook/{apiKey}

# Source-specific webhook
POST https://app.trailspark.ai/api/signal-staging/webhook/{source}/{apiKey}

# Batch webhook
POST https://app.trailspark.ai/api/signal-staging/webhook/{apiKey}/batch

# Bulk ingestion
POST https://app.trailspark.ai/api/signal-staging/bulk/{apiKey}
```

### Example Request

```bash
curl -X POST \
  "https://app.trailspark.ai/api/signal-staging/webhook/YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "lead@example.com",
    "event": "form_submission",
    "properties": {
      "form_name": "Contact Form",
      "page_url": "https://yoursite.com/contact"
    }
  }'
```

## API Key Properties

| Property | Description |
|----------|-------------|
| **Name** | Descriptive label |
| **Key** | Partial key displayed (full key shown only at creation) |
| **Created** | Creation date |
| **Expires** | Expiration date or "Never" |
| **Status** | Active or Deactivated |
| **Last Used** | Timestamp of last webhook request |

## Deactivating Keys

Click **Deactivate** on the key row and confirm. Deactivation immediately rejects all webhook requests using that key.

> [!WARNING]
> Deactivating a key affects all integrations using it. Verify no active systems depend on the key before deactivating.

## Secret Verification

When a secret is configured on the API key, include it in the `X-Api-Secret` request header. TrailSpark hashes the provided secret with SHA256 and compares it to the stored hash.

```bash
curl -X POST \
  "https://app.trailspark.ai/api/signal-staging/webhook/YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Api-Secret: YOUR_SECRET" \
  -d '{"email": "lead@example.com", "event": "page_view"}'
```

If the key has a secret configured and the header is missing or incorrect, the request is rejected with `401`.

## Troubleshooting

**Key not working** -- Verify the full key was copied, check expiration, confirm key is active, and ensure the URL format is correct.

**Request rejected (401)** -- The API key is invalid, expired, or deactivated. Create a new key if needed.

**Request rejected (403)** -- The API key's endpoint type does not match the URL. A signal_staging key cannot be used on the product-orgs endpoint and vice versa.

## Next Steps

- [Webhook Configuration](/docs/webhook-configuration)
- [Webhook Payload Format](/docs/webhook-payload-format)
- [Product Org Updates](/docs/product-org-updates)