---
title: "Issue a project grant, optionally with its first grant key (owner-only)"
method: POST
path: "/projects/v1/{project_id}/grants"
tags: ["Org"]
---

# Issue a project grant, optionally with its first grant key (owner-only)

`POST /projects/v1/{project_id}/grants`

## Request

- Base URL: `https://api.run402.com`
- URL: `https://api.run402.com/projects/v1/{project_id}/grants`
- Auth: API key in header `SIGN-IN-WITH-X`

Owner-only on the project's owning org. Issues a capability grant (e.g. `deploy`, `functions:write`) to a wallet's principal: the permission row for an agent or CI principal that is not an org member. With `key`, also mints the grant's first grant key in the same transaction (a credential narrower than the grant, for an agent without a wallet of its own); the key's `token` is returned ONCE and minting it takes the `grant_key.issue` step-up for a sign-in session. Audited.

## Path parameters

- `project_id` string, required

## Headers

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

## Request body

- object
  - `wallet` string, required
  - `capability` string, required
  - `policy` object
  - `expires_at` string, date-time, nullable
  - `key` GrantKeyRequest: A grant key: a credential minted against exactly one grant, narrower than it (scope, spend cap, expiry), revocable on its own, never an owner.
    - `kind` 'tempo_access_key' | 'base_disposable_eoa' | 'run402_agent_key' | 'ci_oidc': Defaults to run402_agent_key (the bearer an agent holds). erc7710 is forward-declared and rejected (501).
    - `scope` object: Defaults to the grant's own capability. Always a subset of the grant: a key never widens it.
      - `v` 1, required
      - `capabilities` string[], required
      - `projects` string[]
    - `spend_cap` object, nullable: Required for payment rails (tempo/base). usd_micros.
      - `v` 1
      - `currency` 'usd_micros'
      - `period` 'day' | 'week' | 'month'
      - `per_period` integer
      - `per_tx` integer
    - `expires_at` string, date-time, nullable
    - `public_subject` string

## Response `201`

Grant issued

- object
  - `status` string
  - `grant_id` string, uuid
  - `principal_id` string, uuid
  - `key` MintedGrantKey: The minted key. `token` is the run402_agent_key bearer, returned ONCE; null for kinds that carry no bearer.
    - `key_id` string, uuid
    - `kind` string
    - `token` string, nullable: run402_agent_key bearer — returned once.
    - `expires_at` string, date-time, nullable

## Other responses

- `400`: Invalid grant or key request (VALIDATION_FAILED, INVALID_GRANT_KEY_SCOPE, SCOPE_EXCEEDS_GRANT, SPEND_CAP_REQUIRED, INVALID_GRANT_KEY_SUBJECT)
- `403`: Owner role required (FORBIDDEN)

---

[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)
