---
title: "Allocate the project's vault, or push-to-create it (owner / org member)"
method: POST
path: "/vaults/v1"
tags: ["vault"]
---

# Allocate the project's vault, or push-to-create it (owner / org member)

`POST /vaults/v1`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/vaults/v1`
- Auth: one of:
  - API key in header `SIGN-IN-WITH-X`
  - HTTP bearer

Accepts ONE of two mutually-exclusive addressing forms (repo-first-onramp D6). {project_id} (owner-only, gitvault.create): allocates AT MOST ONE vault for an EXISTING project and returns the control-plane-signed allocation record binding the principal, org, project, and the proposed creator key fingerprints. IDEMPOTENT on client_creation_id: replaying the same id returns the same record with deduplicated: true (200 instead of 201), so a crashed creator resumes. Allocation does NOT set the project's vault_policy - a freshly allocated vault leaves it null and deploys proceed ungated until an owner sets it via PATCH .../policy, the ONLY writer of vault_policy. A superseded allocation is refused ALLOCATION_SUPERSEDED; a foreign creation in progress is VAULT_CREATION_CONFLICT. {org_slug, repo_name} (push-to-create): when the project does not exist yet, authorizes via org membership (developer+, the same bar as POST /projects/v1 with an explicit org_id) instead of a project-scoped capability, then atomically claims the repo name, creates the project, and allocates its vault - all in one call. The name-claim is race-safe: a losing concurrent push gets a typed REPO_CREATION_CONFLICT naming the winner's project_id, with a next_actions entry to push again unchanged (never a transparent retarget, never an orphan project row). A slug in its release cooldown is refused SLUG_RELEASED naming the successor.

## Headers

- `SIGN-IN-WITH-X` string, required

## Request body

- object
  - `project_id` string: Existing-project form. Mutually exclusive with org_slug/repo_name.
  - `client_creation_id` string, required: 32 lowercase hex — the idempotency key.
  - `creator_signing_pubkey` string, required: Raw 32-byte Ed25519 public key, canonical base64url.
  - `creator_encryption_pubkey` string, required: Raw 32-byte X25519 public key, canonical base64url.
  - `org_slug` string: Push-to-create form (with repo_name): the target org's claimed slug. Mutually exclusive with project_id.
  - `repo_name` string: Push-to-create form (with org_slug): the address-form repo name to create if it does not already resolve.
  - `storage_profile` 'managed' | 'byo': vault-byo-primary-bucket (D220): where payload ciphertext (WAL packs, checkpoint packs, ref state, retention roots, key envelopes) is client-written. Absent or `managed` is byte-identical to every vault allocated before this field existed — run402's own bucket, gateway-HEAD-verified finalize. `byo` names a customer-owned S3-compatible destination via `byo_destination` (required together): run402 holds zero credentials for it and no request ever carries one; finalize becomes client-attested (`storage_verification: "client_attested"` on payload receipts) instead of gateway-verified; BYO payload bytes never enter the org-pooled storage accounting. Chosen ONLY at allocation in v1 — no route flips it on an existing vault.
  - `byo_destination` string: Required together with `storage_profile: "byo"`; illegal otherwise. The destination's ADDRESS ONLY (bucket/prefix/endpoint-shaped, e.g. `s3://my-bucket/prefix`) — NEVER credential material of any kind. The client resolves the credential (profile name or ambient) locally at use time; it is never transmitted to run402.

## Response `200`

Idempotent replay of the same client_creation_id

- object
  - `repo_id` string
  - `project_id` string
  - `org_id` string, uuid
  - `allocation_generation` integer
  - `allocation` object: The control-plane-signed `allocation` record, exactly as stored.
  - `allocation_sha256` string
  - `storage_profile` 'managed' | 'byo': vault-byo-primary-bucket (D220). See the request body field of the same name.
  - `byo_destination` string, nullable: Present iff storage_profile is `byo`; the destination address only, never credential material.
  - `deduplicated` boolean
  - `next_actions` object[]

## Other responses

- `201`: Vault allocated
- `403`: Not authorized for this vault (VAULT_ACCESS_DENIED) or over quota
- `409`: VAULT_CREATION_CONFLICT / ALLOCATION_SUPERSEDED

---

[API](https://skmtc.dev/run402/apis/run402-api.md) · [All operations](https://skmtc.dev/run402/apis/run402-api/llms.txt) · [OpenAPI document](https://skmtc.dev/run402/apis/run402-api/revisions/893607557f50?raw)
