Actions are synchronous hooks that run *during* an authentication flow. Unlike [events & webhooks](/docs/events-webhooks), which fire after the fact and cannot change the outcome, an action is awaited inline and its response gates the flow: it can **allow**, **deny**, or **enrich** the session with custom token claims.

Use actions to enforce rules your backend owns — block disposable email domains, require an allow-list, call a risk engine, or stamp entitlements into the token — without forking Authdog's hosted flows.

## Triggers

Each action is bound to one trigger:

| Trigger | Fires | Typical use |
| --- | --- | --- |
| `pre_registration` | Before an account is created | Block domains, enforce invite-only |
| `pre_authentication` | Before credentials are verified | Deny known-bad users, geo rules |
| `post_authentication` | After a successful sign-in | Risk scoring, step-up decisions |
| `pre_token_issuance` | Before a token is signed | Inject custom claims (entitlements, roles) |

`post_authentication` and `pre_token_issuance` run for every flow — email/password, social, and enterprise SSO.

## Contract

Authdog `POST`s a JSON body to your endpoint and signs it with an `X-Authdog-Signature: t=<unix>, v1=<hmac-sha256 of "t.body">` header (the same scheme as webhooks). Verify the signature with the action's signing secret, then answer:

```json
{ "decision": "allow", "claims": { "plan": "pro" } }
```

- `decision`: `"allow"` or `"deny"` (required).
- `message`: optional; surfaced to the user on a deny.
- `claims`: optional object merged into the issued token (honored on `pre_token_issuance`).

A `deny` short-circuits the flow immediately. Hooks for the same trigger run in rank order.

## Fail mode

If your endpoint times out, errors, or returns a malformed body, `failMode` decides the outcome:

- `closed` (default): the auth flow is blocked. Choose this for security-critical checks.
- `open`: the flow proceeds as if allowed. Choose this for enrichment that must never take down sign-in.

`timeoutMs` bounds how long Authdog waits (default 3000ms).

## Public API

Actions are managed with your Authdog management API token at
`https://api.authdog.com/v1/tenants/{tenantId}/environments/{environmentId}/actions`.

| Operation | Endpoint |
| --- | --- |
| List | `GET /actions` |
| Create / update | `POST /actions` |
| Test | `POST /actions/test` |
| Logs | `GET /actions/executions` |
| Delete | `DELETE /actions/{id}` |

Create returns the signing secret **once**:

```bash
curl -X POST \
  https://api.authdog.com/v1/tenants/$TENANT/environments/$ENV/actions \
  -H "Authorization: Bearer $AUTHDOG_MANAGEMENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Block disposable emails",
        "hookType": "pre_registration",
        "url": "https://api.acme.com/authdog/hook",
        "failMode": "closed"
      }'
# → { "action": { ... }, "signingSecret": "whsec_…" }
```

Store the secret securely; it is not retrievable later (only a masked hint is returned by the list endpoint). Rotate it with `{ "id": "…", "regenerateSigningSecret": true }`.

## Console

Configure actions under **Actions** in the console. The **Configuration** tab manages hooks and reveals the signing secret on create; the built-in **Test** button fires a sample payload at your URL and shows the decision; the **Logs** tab lists recent invocations with status, HTTP code, and latency.

## Related

- [Events & webhooks](/docs/events-webhooks)
- [JWT claims](/docs/jwt-claims)
- [Bot protection](/docs/bot-protection)
- [Audit logs](/docs/audit-logs)
