---
title: "Ask permission to perform a side effect"
method: POST
path: "/v1/effects/begin"
tags: ["Effects"]
---

# Ask permission to perform a side effect

`POST /v1/effects/begin`

## Request

- Base URL: `https://ratchetgate.com`
- URL: `https://ratchetgate.com/v1/effects/begin`
- Auth: HTTP bearer

The gate. Returns a decision that tells the caller whether to perform the action, replay a recorded result, wait, or stop. Call this immediately BEFORE the side effect, and report the outcome immediately after.

## Request body

- object
  - `effect_type` string, required: Namespaced kind of side effect, e.g. "email.send" or "payment.charge". Policy is configured per type.
  - `idempotency_key` string, required: Stable identifier for this logical action. Derive it from the work itself (e.g. a hash of recipient + template + period), never from a random value or a timestamp.
  - `payload` unknown
  - `estimated_cost_micros` integer: Declared external cost of this effect in micro-USD (1e-6 USD). SEND THIS. Budget ceilings are computed from it, and a ceiling with nothing declared against it never triggers — the spend limit you configured would be silently inert. It is also what makes the prevented-loss figure meaningful. Ratchet never collects this money; it only counts it.
  - `agent_id` string: Which agent is acting. For operator visibility.
  - `run_id` string: Groups effects belonging to one agent run.
  - `request_summary` object: Small, non-sensitive metadata shown in the console. Redact secrets before sending.
  - `dimensions` object: Axes this effect can be counted against, most usefully the counterparty: {"counterparty":"acct_1234"}. Send the identifier itself — only a keyed hash is stored, so Ratchet can count how much has gone to a destination today without ever being able to say which destination it is. A declaration can only TIGHTEN: it adds whatever ceiling policy keys on that dimension, and never removes the workspace, key or effect-type ceilings. Omitting one that policy requires is refused with `dimension_required`.
  - `lease_seconds` integer: Requested lease length. Clamped to the policy maximum for this effect type.
  - `vendor` string: Which vendor performs this effect (e.g. "stripe", "square", "adyen"). Shapes the vendor_idempotency_key returned with an execute decision so it satisfies that vendor's length and placement rules.
  - `group_key` string: Declares this effect part of a unit of work that can be rolled back as a whole. Use one stable key per logical workflow, e.g. "booking:trip_8812".
  - `compensation` object: How to undo this effect if the unit of work has to be rolled back. Declare it now, while you still know what undoing means — it cannot be reconstructed later.
    - `effect_type` string, required
    - `payload` unknown, required
  - `compensates_effect_id` string: Set when THIS effect is the compensation for another. Reporting it succeeded marks the original reversed.

## Response `200`

Default Response

- object
  - `decision` 'execute' | 'duplicate' | 'in_flight' | 'blocked' | 'approval_required' | 'denied', required: execute: you hold the lease, perform the effect. duplicate: already done, replay `result`, do NOT perform it. in_flight: another caller holds a live lease. blocked: a prior attempt's real-world outcome is unknown. approval_required: an operator must approve first. denied: policy, budget, or a rejected approval refused it.
  - `effect_id` string, required
  - `effect_type` string, required
  - `idempotency_key` string, required
  - `state` 'awaiting_approval' | 'pending' | 'succeeded' | 'failed' | 'indeterminate' | 'denied' | 'cancelled', required
  - `attempt` integer, required
  - `lease_token` string: Present only when decision is "execute". Required to report the outcome.
  - `budget_warning` string: Present when a spend ceiling is configured for this effect type but this call declared no cost, so nothing counted toward it. The ceiling cannot trigger until callers send estimated_cost_micros.
  - `integration_warning` string: Present when this workspace has never reported an outcome and unreported effects are accumulating. Every begin must be followed by POST /v1/effects/{effect_id}/report. Unreported effects become "indeterminate" when their lease expires, and the next attempt on the same idempotency_key is then blocked until an operator resolves it.
  - `workspace` object: Present ONLY on a keyless first call, which provisions a workspace on the spot. Store api_key — it is never returned again. The workspace is capped until you claim it with an email at POST /v1/workspaces/claim.
    - `api_key` string
    - `workspace_id` string
    - `quota` integer: Gated effects allowed before claiming.
  - `vendor_idempotency_key` object: Present only with an "execute" decision. Send `key` to the vendor as its own idempotency key. Where `enforced` is true the VENDOR refuses the duplicate, so the guarantee no longer depends on the agent choosing to ask us first. Derived per attempt: retrying this attempt reuses the key, while a genuine retry after a reported failure gets a new one so the vendor does not replay the old failure.
    - `key` string
    - `vendor` string
    - `placement` string: Where to put it in the vendor request.
    - `enforced` boolean: True only where the vendor actually deduplicates on it.
    - `note` string
  - `lease_expires_at` string, date-time
  - `result` unknown
  - `retry_after_seconds` integer
  - `reason` string
  - `prior_attempt` object
    - `attempt` integer
    - `state` string
    - `started_at` string, date-time
    - `last_known_at` string, date-time
    - `on_indeterminate` 'block' | 'retry' | 'probe'
  - `billing` object
    - `metered` boolean: True only when this call created a new gated effect.
    - `included_remaining` integer, nullable

## Other responses

- `400`: Invalid request.
- `401`: Missing or invalid API key.
- `402`: Insufficient prepaid credit.
- `403`: Scope, policy, or budget refusal.
- `404`: Not found.
- `409`: Conflicting state or idempotency-key reuse.
- `413`: Payload too large.
- `429`: Rate limit exceeded.

---

[API](https://skmtc.dev/ratchetgate/apis/ratchet-api.md) · [All operations](https://skmtc.dev/ratchetgate/apis/ratchet-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ratchetgate/apis/ratchet-api/revisions/5712f33c692f?raw)
