---
title: "Add a bucket: AWS S3 (with its region), Cloudflare R2 (with the account ID) or Google Cloud Storage (HMAC keys)."
method: POST
path: "/v1/organizations/current/warehouse/destinations"
tags: ["events"]
---

# Add a bucket: AWS S3 (with its region), Cloudflare R2 (with the account ID) or Google Cloud Storage (HMAC keys).

`POST /v1/organizations/current/warehouse/destinations`

## Request

- Base URL: `https://api.50heads.com`
- URL: `https://api.50heads.com/v1/organizations/current/warehouse/destinations`
- Auth: HTTP bearer

Organization admins only (403 `organization_admin_required`), on a browser session with a fresh proof in the `x-50h-reauth` header (401 `reauth_required`). It needs a person: API keys, connected apps and service accounts get 403 `session_required`. Every change is written to the organization's audit. Saving writes a small test file under the prefix first; if the bucket refuses it nothing is saved (422 `destination_unreachable`, with the test in `details.test`). The keys need permission to put objects under the prefix and nothing else. The first run is within five minutes. Up to 5 destinations (409 `too_many_destinations`). Behind the `warehouseExport` flag (and the organizations flag): 404 `not_found` until it is on for your organization. Each run writes JSON lines under `<prefix><dataset>/dt=<date>/<run>-<n>.jsonl` and then a manifest at `<prefix>manifests/<time>-<run>.json` listing the files, their rows and SHA-256; load the files a manifest lists (a file with no manifest is from a run that failed and is written again). Parquet is not written. Keys are stored encrypted and never returned.

## Request body

- WarehouseDestinationCreateRequest
  - `name` string, required
  - `provider` 's3' | 'r2' | 'gcs', required
  - `bucket` string, required
  - `region` string
  - `accountId` string
  - `jurisdiction` 'default' | 'eu' | 'fedramp'
  - `prefix` string
  - `datasets` string[]
  - `intervalMinutes` union
    - 60
    - 360
    - 1440
  - `accessKeyId` string, required
  - `secretAccessKey` string, required

## Response `201`

OK

- WarehouseDestinationCreateResponse
  - `destination` object, required
    - `id` string, required
    - `name` string, required
    - `provider` 's3' | 'r2' | 'gcs', required
    - `bucket` string, required
    - `region` string, nullable, required
    - `accountId` string, nullable, required
    - `jurisdiction` 'default' | 'eu' | 'fedramp', nullable, required
    - `prefix` string, required
    - `accessKeyHint` string, required
    - `datasets` string[], required
    - `intervalMinutes` integer, required
    - `state` 'active' | 'paused' | 'failing', required
    - `consecutiveFailures` integer, required
    - `lastError` string, nullable, required
    - `lastRunAt` string, nullable, required
    - `lastSuccessAt` string, nullable, required
    - `nextRunAt` string, nullable, required
    - `exportedThrough` object, required
      - `events` string, nullable, required
      - `audit` string, nullable, required
    - `createdAt` string, required
    - `createdBy` object, required
      - `id` string, required
      - `name` string, nullable, required
  - `test` object, required
    - `ok` boolean, required
    - `status` integer, nullable, required
    - `code` 'access_denied' | 'no_such_bucket' | 'invalid_credentials' | 'wrong_region' | 'unreachable' | 'bucket_error', nullable, required
    - `message` string, required
    - `key` string, required

## Other responses

- `400`: The request is not valid; `error.details.validation` lists the fixes.
- `401`: No key or token, or it expired or was revoked.
- `403`: The key or token lacks the scope (`insufficient_scope`), or the action is not allowed.
- `429`: Too many requests; wait for Retry-After.

---

[API](https://skmtc.dev/50heads/apis/50heads-api.md) · [All operations](https://skmtc.dev/50heads/apis/50heads-api/llms.txt) · [OpenAPI document](https://skmtc.dev/50heads/apis/50heads-api/revisions/20175e8b00e3?raw)
