# Managing API Keys

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

## Accessing API Keys

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

> [!NOTE]
> API Keys only appears in the sidebar for Owners and Admins. Other members who navigate to the URL directly see an explanation that only workspace owners and admins can view or manage API keys, instead of the keys list.

## 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 — not even from the keys list afterward. If you lose a key, create a new one and deactivate the old one.

## 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](https://docs.trailspark.ai/webhooks-api/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

The **Your API Keys** list shows:

> Keys are shown in full only when they're created. If you've lost a key, create a new one and deactivate the old one.

| Property | Description |
| - | - |
| **Name** | Descriptive label |
| **Key** | Shortened to the first 8 and last 4 characters (e.g., `sk_abcd1234...wxyz`) — the full key is never shown again after creation, and there's no copy button on this list |
| **Created** | Creation date |
| **Expires** | Expiration date or "Never" |
| **Status** | Active or Inactive |
| **Last Used** | Timestamp of last webhook request |

The list includes both active and deactivated keys, so you can confirm a key's real status at a glance.

## Deactivating Keys

Click **Deactivate** on the key row and confirm. Deactivation immediately rejects all webhook requests using that key. The key remains visible in the list afterward, marked **Inactive**.

> [!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](https://docs.trailspark.ai/webhooks-api/webhook-configuration)
- [Webhook Payload Format](https://docs.trailspark.ai/webhooks-api/webhook-payload-format)
- [Product Org Updates](https://docs.trailspark.ai/webhooks-api/product-org-updates)
