> ## Documentation Index
> Fetch the complete documentation index at: https://docs.olira.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create action destination

> Registers a webhook or email destination and subscribes it to the triggers you care about.

<CodeGroup>
  ```python Python theme={null}
  olira.create_action_destination(
      config: WebhookDestinationConfig | EmailDestinationConfig | dict,
      subscribed_triggers: list[ActionTrigger | str] | None = None,
      description: str | None = None,
      static_headers: dict[str, str] | None = None,
      rate_limit_per_minute: int | None = 600,
      digest_schedule: DigestSchedule | None = None,
  ) -> ActionDestination
  ```

  ```csharp C# theme={null}
  OliraModule.CreateActionDestination(
      WebhookDestinationConfig? webhookConfig = null,
      EmailDestinationConfig? emailConfig = null,
      IReadOnlyList<string>? subscribedTriggers = null,
      string? description = null,
      IReadOnlyDictionary<string, string>? staticHeaders = null,
      int? rateLimitPerMinute = null,
      DigestSchedule? digestSchedule = null,
  )  // -> ActionDestination
  ```
</CodeGroup>

**Requires scope:** `sdk:actions`

The signing secret is returned in plaintext exactly once — store it immediately, it can only be rotated afterward, never re-read.

## Parameters

<ParamField body="config" type="WebhookDestinationConfig | EmailDestinationConfig | dict" required>
  Destination type and its config: WebhookDestinationConfig (url, optional api\_version) or EmailDestinationConfig (to\_email, optional subject/from\_name). A raw dict is accepted for destination types not yet modeled by this SDK version.
</ParamField>

<ParamField body="subscribed_triggers" type="list[ActionTrigger | str] | None">
  Triggers this destination receives. \["\*"] subscribes to every currently available trigger, and, because it is evaluated by the platform rather than by this list, could start receiving additional trigger types later without another call on your part.
</ParamField>

<ParamField body="description" type="str | None">
  Free-text label shown in the Olira Console.
</ParamField>

<ParamField body="static_headers" type="dict[str, str] | None">
  Extra headers replayed on every request, e.g. an API key your endpoint expects. Write-only — never read back.
</ParamField>

<ParamField body="rate_limit_per_minute" type="int | None" default="600">
  Per-destination delivery cap, 1–6000.
</ParamField>

<ParamField body="digest_schedule" type="DigestSchedule | None">
  Opt in to daily batching for high-frequency triggers instead of one delivery per event.
</ParamField>

## Returns

`ActionDestination` — Created destination, including the one-time signing\_secret.

<ResponseField name="id" type="str">
  Olira-assigned destination id.
</ResponseField>

<ResponseField name="project_id" type="str | None">
  Project this destination watches. None = the org's default project.
</ResponseField>

<ResponseField name="destination_type" type="str">
  "webhook" or "email".
</ResponseField>

<ResponseField name="status" type="str">
  "active", "disabled", or "auto\_disabled".
</ResponseField>

<ResponseField name="description" type="str | None">
  Free-text description.
</ResponseField>

<ResponseField name="subscribed_triggers" type="list[str]">
  Triggers this destination receives.
</ResponseField>

<ResponseField name="config" type="dict">
  Type-specific config as returned by the server (url, api\_version, etc).
</ResponseField>

<ResponseField name="signing_secret_last4" type="str | None">
  Last 4 characters of the current signing secret.
</ResponseField>

<ResponseField name="rate_limit_per_minute" type="int | None">
  Per-destination delivery rate cap.
</ResponseField>

<ResponseField name="digest_schedule" type="DigestSchedule | None">
  Digest batching config, if enabled.
</ResponseField>

<ResponseField name="consecutive_failures" type="int">
  Running failure streak; resets to 0 on any successful delivery.
</ResponseField>

<ResponseField name="auto_disabled_at" type="str | None">
  When the destination was auto-disabled (20+ consecutive failures over 72h+), if applicable.
</ResponseField>

<ResponseField name="rotated_at" type="str | None">
  When the signing secret was last rotated.
</ResponseField>

<ResponseField name="signing_secret" type="str | None">
  Plaintext signing secret — present only here and on rotate\_action\_destination\_secret responses.
</ResponseField>

## Raises

| Exception         | When                                                                                                                  |
| ----------------- | --------------------------------------------------------------------------------------------------------------------- |
| `AuthError`       | HTTP 401 or 403 — invalid API key or insufficient OAuth scope for this endpoint.                                      |
| `ValidationError` | HTTP 400, 404, or 422 — malformed JSON or validation failure (message includes response excerpt).                     |
| `ServerError`     | HTTP 409 (e.g. conflicting external identifier) or repeated 5xx after retries; includes status\_code when applicable. |
| `RateLimitError`  | HTTP 429 — rate limited; check retry\_after (seconds).                                                                |
| `NetworkError`    | Connection timeout, DNS failure, or read error after retries.                                                         |

<RequestExample>
  ```python Python theme={null}
  import olira
  from olira import ActionTrigger, WebhookDestinationConfig

  olira.init(api_key="YOUR_API_KEY")
  destination = olira.create_action_destination(
      config=WebhookDestinationConfig(url="https://hooks.example.com/olira"),
      subscribed_triggers=[ActionTrigger.PATIENT_STATE_CHANGED, ActionTrigger.INGESTION_FAILED],
  )
  print(destination.signing_secret)  # shown once, store it now
  ```

  ```csharp C# theme={null}
  using Olira;

  OliraModule.Init(apiKey: "YOUR_API_KEY");

  var destination = OliraModule.CreateActionDestination(
      webhookConfig: new WebhookDestinationConfig { Url = "https://hooks.example.com/olira" },
      subscribedTriggers: [ActionTrigger.PatientStateChanged, ActionTrigger.IngestionFailed]);
  Console.WriteLine(destination.SigningSecret); // shown once, store it now
  ```

  ```http HTTP theme={null}
  POST /v1/actions/destinations
  Authorization: Bearer YOUR_API_KEY
  Content-Type: application/json

  {
    "config": {"destination_type": "webhook", "url": "https://hooks.example.com/olira"},
    "subscribed_event_types": ["patient.state.changed", "ingestion.failed"]
  }
  ```
</RequestExample>

<ResponseExample>
  ```python 200 theme={null}
  ActionDestination(
      id="68582e4a0a0b3c1234567890",
      project_id=None,
      destination_type="webhook",
      status="active",
      description=None,
      subscribed_triggers=["patient.state.changed", "ingestion.failed"],
      config={"destination_type": "webhook", "url": "https://hooks.example.com/olira", "api_version": "2026-08-01"},
      signing_secret_last4="wxlA",
      rate_limit_per_minute=600,
      digest_schedule=None,
      consecutive_failures=0,
      auto_disabled_at=None,
      rotated_at=None,
      signing_secret="whsec_C-8mLEFGnat57TspFl581aGumYpI2dNzKxdi-9Iu1UI",
  )
  ```

  ```json 401 theme={null}
  {
    "error": true,
    "status_code": 401,
    "error_type": "authentication_error",
    "message": "Could not validate credentials",
    "details": [
      {
        "type": "authentication_error",
        "message": "Could not validate credentials"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```

  ```json 403 theme={null}
  {
    "error": true,
    "status_code": 403,
    "error_type": "authorization_error",
    "message": "Insufficient OAuth scope for this endpoint",
    "details": [
      {
        "type": "authorization_error",
        "message": "Insufficient OAuth scope for this endpoint"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```

  ```json 404 theme={null}
  {
    "error": true,
    "status_code": 404,
    "error_type": "not_found_error",
    "message": "Destination not found",
    "details": [
      {
        "type": "not_found_error",
        "message": "Destination not found"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```

  ```json 422 theme={null}
  {
    "error": true,
    "status_code": 422,
    "error_type": "validation_error",
    "message": "Request validation failed (1 error)",
    "details": [
      {
        "type": "value_error",
        "message": "Request validation failed"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```

  ```json 429 theme={null}
  {
    "error": true,
    "status_code": 429,
    "error_type": "server_error",
    "message": "Rate limit exceeded",
    "details": [
      {
        "type": "rate_limit",
        "message": "Too many requests; retry after backoff"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```

  ```json 500 theme={null}
  {
    "error": true,
    "status_code": 500,
    "error_type": "internal_server_error",
    "message": "An internal server error occurred",
    "details": [
      {
        "type": "internal_server_error",
        "message": "An unexpected error occurred while processing your request"
      }
    ],
    "timestamp": "2026-05-06T12:00:00+00:00"
  }
  ```
</ResponseExample>
