# Welcome

ToolRegistry lets agents go beyond one-off tool calls. It discovers, sequences, and executes multi-step actions across APIs and SaaS apps while keeping you in control with endpoint allow‑lists, approvals, audit logs, and instant revocation.

**Why teams use ToolRegistry**

* **Automatic orchestration**: no brittle flowcharts or glue code. The system plans and executes tool chains at runtime.
* **Open-world agents, enterprise guardrails**: dynamic tool discovery within strict boundaries you define.
* **Tenant-aware by design**: learns each customer’s objects/fields and adapts to schema drift automatically.
* **Connect everything**: managed integrations for popular apps plus custom REST/OpenAPI and remote MCP.

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-bolt">:bolt:</i></h4></td><td><strong>Quickstart</strong></td><td>Build your first end‑to‑end automation in ~15 minutes.</td><td></td><td></td><td><a href="/getting-started/quickstart">Quickstart</a></td></tr><tr><td><h4><i class="fa-head-side-brain">:head-side-brain:</i></h4></td><td>Mental model</td><td>How intent > action works</td><td></td><td></td><td><a href="/basics/mental-model">Mental Model</a></td></tr><tr><td><h4><i class="fa-globe-pointer">:globe-pointer:</i></h4></td><td><strong>API Reference</strong></td><td>Checkout the API and try our playground</td><td></td><td></td><td><a href="/api-documentation">API Documentation</a></td></tr></tbody></table>


# Quickstart

| `NONE` \| `ON_START` \| `ON_TOOL_CALL` (defaults to `NONE`). |
| ------------------------------------------------------------ |

{% hint style="info" %}
Check out the [Mental Model](/basics/mental-model) and [Core Objects](/basics/core-objects) to make your life easier
{% endhint %}

### Prerequisites

* **API key** with access to your workspace (`x-api-key`).
* At least **one integration enabled** (managed or custom). For first runs, prefer a managed and read-only integration.
* OpenAPI spec is downloadable [here](https://openapi.gitbook.com/o/35ZmqYj6yWUW4rLOFJSa/spec/toolregistry-api.json).

### Enable an integration

1. Go to [integrations](https://app.toolregistry.ai/integrations)&#x20;
2. &#x20;Enable an integration (or upload a custom integration) - For this demo we recommend "Google Calendar" or "Outlook Calendar"

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2FdIUaDi2Z68DtFMHDXJXz%2FScreenshot%202025-08-31%20at%2008.40.57.png?alt=media&amp;token=914132b2-b793-49d1-aec1-615e02c643d7" alt=""><figcaption></figcaption></figure>

### Get your API key

1. Head over to [API keys section](https://app.toolregistry.ai/api-keys) in console&#x20;

   <figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2Fmgs9PKcTjifAkGmnpj49%2FScreenshot%202025-08-31%20at%2008.46.20.png?alt=media&amp;token=4ed627c2-f3bf-4db6-8643-76e517c248ed" alt=""><figcaption></figcaption></figure>

2. Generate an API key

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2FZ4Uy7EU6istJdGu2HNiZ%2FScreenshot%202025-08-31%20at%2008.52.31.png?alt=media&amp;token=802be313-72fb-47f9-836e-d6e81be6d071" alt=""><figcaption></figcaption></figure>

3. Copy and Save the API key

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2F2Aw8UtLd9gyx4Q5yVyrI%2FScreenshot%202025-08-31%20at%2008.49.16.png?alt=media&amp;token=55a18c6e-0d80-4679-b4d7-1d0a87d216e9" alt=""><figcaption></figcaption></figure>

3. Export the api Key

```sh
export TR_API_KEY="sk_live_..."  
export TR_API_URL="https://api.toolregistry.ai"
```

### Create your first Run

A **Run** is the execution of an intent with guardrails.

**Request Fields**

<table><thead><tr><th width="265.17578125">Field</th><th>Type</th><th width="116.26171875">Required</th><th>Notes</th></tr></thead><tbody><tr><td>intent</td><td>String</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span></td><td>What outcome you want (natural language).</td></tr><tr><td>context</td><td>String</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span></td><td>Hidden, technical details (timezone, constraints). Keep sensitive data here; don’t show to end users.</td></tr><tr><td>userId</td><td>String</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span></td><td>Identity whose tokens/permissions apply (act‑as‑user).</td></tr><tr><td>selectedCustomerIntegrationIds</td><td>string[]</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span></td><td>Allow‑list of connections, or <code>["*"]</code> for all active.</td></tr><tr><td>approvalStrategy</td><td>enum</td><td>-</td><td><code>NONE</code> | <code>ON_START</code> | <code>ON_TOOL_CALL</code> (defaults to <code>NONE</code>).</td></tr><tr><td>outputSchema</td><td>object</td><td>-</td><td>Define the expected output format by providing JSON Schema Draft-07 object</td></tr></tbody></table>

Example intent (declarative, long‑form)

{% code overflow="wrap" %}

```
Book a meeting in Google or Outlook calendar for next thursday at 12:00 - Name "Lunch  with myself without a screen".
```

{% endcode %}

### Create the run (cURL):

```sh
RUN_PAYLOAD='{
"intent": "Book a meeting in Google or Outlook calendar for next thursday at 12:00 - Name "Lunch  with myself without a screen",
"context": "timezone=Europe/Copenhagen; window=next_week; one_per_day=true; stage=Proposal Made; staleness_days=14; meeting_time=12:00; slack_channel=#demo-update",
"userId": "user-123",
"selectedCustomerIntegrationIds": ["*"]
}'


curl -s -X POST "$TR_API_URL/v1/runs" \
-H "x-api-key: $TR_API_KEY" \
-H "Content-Type: application/json" \
-d "$RUN_PAYLOAD"
```

### Watch the live execution

```sh
RUN_ID="<uuid-from-create>"
curl -N -H "x-api-key: $TR_API_KEY" \
"$TR_API_URL/v1/runs/$RUN_ID/sse"
```

### Inspect the Run and result

```
curl -s -H "x-api-key: $TR_API_KEY" \
"$TR_API_URL/v1/runs/$RUN_ID" | jq
```

### Production tips

* **Intent**: be outcome-focused and testable. Prefer declarative language (what), let the system decide (how).
* **Context**: keep sensitive details here; do not surface to end users.
* **Approvals**: start with `ON_TOOL_CALL` for writes for your agent; `ON_START` if you need human to review the plan.
* **Least privilege**: choose a `userId` with only the permissions needed.
* **Scoping**: For QuickStart user \["\*"] for integrations to select all that have been enabled
* **Observability**: use SSE in staging/early prod to watch real behavior.


# Remote MCP

Setting up and using our remote MCP

ToolRegistry has a remote MCP endpoint available at <https://mcp.toolregistry.ai/mcp> that supports streamable HTTP for transport.

### Prerequisites ([Quickstart](/getting-started/quickstart))

* API key
* At least **one active** integration

### MCP configuration

{% code title="mcp.json" %}

```json
{
  "mcpServers": {
    "ToolRegistry": {
      "type": "streamable-http",
      "url": "https://mcp.toolregistry.ai/mcp",
      "headers": {
        "x-api-key": "<your_api_key>",
        "x-user-id": "<user_identifier>"
      }
    }
  }
}

```

{% endcode %}

The **x-user-id** header is a unique identifier for a user.&#x20;

This should be unique per end user. When using remote MCP on your local machine through an app like Claude, Cursor, etc, then this can be set to a single static value. \
The `x-user-id` header is required when using OAuth based integrations.\
See more about the [user identifier](/concepts/act-as-user-userid)

### Tools

Remote MCP provides the following tools

#### List integrations

Lists all the active integrations that you have available

#### Execute intent

Execute an intent across any of your active integrations. \
Returns information about a created run that can be used to check the status of the run.

#### Run status

Allows to check the status of a run by ID.\
Optionally can include all events such as all tool-calls made during the run.


# API Documentation


# Integration

## Get All Integrations

> Retrieves a list of all integrations, optionally filtered by active status

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/integrations":{"get":{"description":"Retrieves a list of all integrations, optionally filtered by active status","operationId":"IntegrationController_getAllIntegrations","parameters":[{"name":"active","required":false,"in":"query","schema":{"default":true,"type":"boolean"}}],"responses":{"200":{"description":"Integrations retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/IntegrationResponseDto"}}}}},"401":{"description":"Unauthorized - invalid API key"},"500":{"description":"Internal server error"}},"summary":"Get All Integrations","tags":["Integration"]}}},"components":{"schemas":{"IntegrationResponseDto":{"type":"object","properties":{"integrationId":{"type":"string"},"customerIntegrationId":{"type":"string"},"active":{"type":"boolean"},"name":{"type":"string"},"description":{"type":"string"},"logoUrl":{"type":"string"},"host":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"},"authType":{"type":"string","enum":["NONE","API_TOKEN","OAUTH2","AWS_ASSUME_ROLE","GOOGLE_SERVICE_ACCOUNT_OAUTH"],"description":"Currently active authentication type for the integration."}},"required":["integrationId","customerIntegrationId","active","name","host","updatedAt","createdAt"]}}}}
```

## Get Integration User

> Retrieves a user for a specific customer integration

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/integrations/{customerIntegrationId}/users/{userId}":{"get":{"description":"Retrieves a user for a specific customer integration","operationId":"IntegrationController_getIntegrationUser","parameters":[{"name":"customerIntegrationId","required":true,"in":"path","description":"Customer integration ID","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","description":"User ID associated with the integration","schema":{"type":"string"}}],"responses":{"200":{"description":"Integration user retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationUserResponseDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"500":{"description":"Internal server error"}},"summary":"Get Integration User","tags":["Integration"]}}},"components":{"schemas":{"IntegrationUserResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"active":{"type":"boolean"},"updatedAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"}},"required":["userId","active","updatedAt","createdAt"]}}}}
```

## Generate Integration User Auth URL

> Generates an auth URL for a specific integration user. User will be created if it does not exist for the integration.

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/integrations/{customerIntegrationId}/users/{userId}/auth-url":{"post":{"description":"Generates an auth URL for a specific integration user. User will be created if it does not exist for the integration.","operationId":"IntegrationController_generateIntegrationUserAuthUrl","parameters":[{"name":"customerIntegrationId","required":true,"in":"path","description":"Customer integration ID","schema":{"type":"string"}},{"name":"userId","required":true,"in":"path","description":"User ID associated with the integration","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationUserAuthUrlRequestDto"}}}},"responses":{"201":{"description":"Auth URL generated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationUserAuthUrlResponseDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"500":{"description":"Internal server error"}},"summary":"Generate Integration User Auth URL","tags":["Integration"]}}},"components":{"schemas":{"IntegrationUserAuthUrlRequestDto":{"type":"object","properties":{"redirectUri":{"type":"string","description":"The optional redirect URI to redirect the user to after authentication. Askel will append query param ?status=success/error. If not provided, a default success/failure page will be shown."}}},"IntegrationUserAuthUrlResponseDto":{"type":"object","properties":{"authorizeUrl":{"type":"string"}},"required":["authorizeUrl"]}}}}
```


# Run

## List Runs

> Retrieves a paginated list of runs for the authenticated user

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs":{"get":{"description":"Retrieves a paginated list of runs for the authenticated user","operationId":"RunController_listRuns","parameters":[{"name":"includeEvents","required":false,"in":"query","schema":{"default":false,"type":"boolean"}},{"name":"userId","required":false,"in":"query","description":"Filter runs by user ID","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter runs by a single status value","schema":{"enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"],"type":"string"}},{"name":"customerIntegrationIds","required":false,"in":"query","description":"Filter runs by customer integration IDs. Use repeated query params, for example: ?customerIntegrationIds=salesforce_abcd1234&customerIntegrationIds=hubspot_efgh5678","schema":{"type":"array","items":{"type":"string"}}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"maximum":1000,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":10,"type":"number"}}],"responses":{"200":{"description":"Runs retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunsWithPaginationDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"500":{"description":"Internal server error"}},"summary":"List Runs","tags":["Run"]}}},"components":{"schemas":{"RunsWithPaginationDto":{"type":"object","properties":{"runs":{"description":"Runs matching the query","type":"array","items":{"$ref":"#/components/schemas/RunDto"}},"pagination":{"description":"Pagination metadata for the result set","allOf":[{"$ref":"#/components/schemas/PaginationDto"}]}},"required":["runs","pagination"]},"RunDto":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"repeatRunId":{"type":"string","description":"The UUID of the run that was repeated","format":"uuid"},"userId":{"type":"string","description":"The ID of the user who initiated the run"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"result":{"type":"object","description":"Result of the run execution"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for run input format","format":"json-schema"},"input":{"type":"object","description":"Input provided when the run was created"},"intent":{"type":"string","description":"The intent or goal for the run"},"context":{"type":"string","description":"Additional context that was provided during run creation"},"createdAt":{"type":"string","description":"Timestamp when the run was created","format":"date-time"},"updatedAt":{"type":"string","description":"Timestamp when the run was last updated","format":"date-time"}},"required":["runId","repeatKey","status","executionPlanId","events","intent","context","createdAt","updatedAt"]},"PaginationDto":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"pageSize":{"type":"number","description":"Number of runs requested per page"},"total":{"type":"number","description":"Total number of runs matching the query"}},"required":["page","pageSize","total"]}}}}
```

## Create a new Run

> Creates a new run with the specified intent and selected integrations

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs":{"post":{"description":"Creates a new run with the specified intent and selected integrations","operationId":"RunController_createRun","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunRequest"}}}},"responses":{"201":{"description":"Run successfully created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunResponse"}}}},"400":{"description":"Invalid request body or parameters"},"401":{"description":"Unauthorized - invalid API key"}},"summary":"Create a new Run","tags":["Run"]}}},"components":{"schemas":{"CreateRunRequest":{"type":"object","properties":{"intent":{"type":"string","description":"The intent or goal for the run"},"selectedCustomerIntegrationIds":{"description":"Array of selected customer integration IDs to use for the run, or use [\"*\"] to include all active integrations","type":"array","items":{"type":"string"}},"context":{"type":"string","description":"Additional context for the run execution"},"userId":{"type":"string","description":"The ID of the user who is initiating the run"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"repeatKey":{"type":"string","description":"The key of the run to repeat","format":"string"},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"passThrough":{"description":"Pass through data for the run, e.g. credentials for the integrations used in the run","type":"array","items":{"$ref":"#/components/schemas/CustomerProvidedPassThrough"}}},"required":["intent","selectedCustomerIntegrationIds","context","userId"]},"CustomerProvidedPassThrough":{"type":"object","properties":{"customerIntegrationId":{"type":"string","description":"The ID of the customer integration this pass-through data applies to."},"headers":{"type":"object","description":"Custom headers to include in requests for this integration."},"query":{"type":"object","description":"Custom query parameters to include in requests for this integration."},"values":{"description":"Provider-specific auth credentials. Use one of: AWS temporary credentials, AWS assume-role, or Okta service app (private_key_jwt). Only one type may be supplied per integration.","oneOf":[{"$ref":"#/components/schemas/CustomerProvidedAwsCredentials"},{"$ref":"#/components/schemas/CustomerProvidedAwsAssumeRole"},{"$ref":"#/components/schemas/CustomerProvidedOktaCredentials"}]}},"required":["customerIntegrationId"]},"CustomerProvidedAwsCredentials":{"type":"object","properties":{"accessKeyId":{"type":"string","description":"The AWS access key ID for authenticating requests."},"secretAccessKey":{"type":"string","description":"The AWS secret access key paired with the access key ID."},"sessionToken":{"type":"string","description":"The temporary session token for AWS STS credentials."}},"required":["accessKeyId","secretAccessKey","sessionToken"]},"CustomerProvidedAwsAssumeRole":{"type":"object","properties":{"roleArn":{"type":"string","description":"The ARN of the IAM role to assume for cross-account access."},"externalId":{"type":"string","description":"An external ID used to prevent confused deputy attacks when assuming the role."}},"required":["roleArn","externalId"]},"CustomerProvidedOktaCredentials":{"type":"object","properties":{"clientId":{"type":"string","description":"The Okta application client ID."},"kid":{"type":"string","description":"The key ID (kid) of the private key used to sign client assertions."},"privateKey":{"type":"string","description":"The PEM-encoded RSA private key used to sign client assertions (private_key_jwt)."},"oauthTokenUrl":{"type":"string","description":"The Okta OAuth 2.0 token endpoint URL for your organisation."},"scope":{"type":"string","description":"Space-separated list of Okta API scopes to request."},"host":{"type":"string","description":"Base host URL of your Okta organisation. If omitted, derived from oauthTokenUrl."}},"required":["clientId","kid","privateKey","oauthTokenUrl","scope"]},"CreateRunResponse":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the created run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"plan":{"description":"Execution plan for the run","allOf":[{"$ref":"#/components/schemas/ExecPlanResponseDto"}]},"repeatRunId":{"type":"string","description":"The UUID of the run that is going to be repeated.","format":"uuid"}},"required":["runId","repeatKey","status","events","executionPlanId","plan"]},"ExecPlanResponseDto":{"type":"object","properties":{"planType":{"type":"string","description":"Plan serialization type","enum":["json","text"]},"plan":{"description":"Execution plan details","oneOf":[{"$ref":"#/components/schemas/ExecPlanDto"},{"type":"string"}]}},"required":["planType","plan"]},"ExecPlanDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional human-readable title for the execution plan"},"description":{"type":"string","description":"High-level description of the execution plan"},"steps":{"description":"Ordered list of steps to execute the plan","type":"array","items":{"$ref":"#/components/schemas/ExecPlanStepDto"}}},"required":["description","steps"]},"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```

## Approve or deny a run or its individual steps

> Approve or deny a run or its individual steps

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs/approval":{"get":{"description":"Approve or deny a run or its individual steps","operationId":"RunController_runApproval","parameters":[{"name":"token","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"201":{"description":"Approval handled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/"}}}},"400":{"description":"Invalid request body or parameters"},"401":{"description":"Unauthorized - invalid API key"}},"summary":"Approve or deny a run or its individual steps","tags":["Run"]}}},"components":{"schemas":{}}}
```

## Get Run details

> Retrieves detailed information about a specific run

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs/{runId}":{"get":{"description":"Retrieves detailed information about a specific run","operationId":"RunController_getRun","parameters":[{"name":"runId","required":true,"in":"path","description":"Unique identifier of the run to retrieve","schema":{"format":"uuid","type":"string"}},{"name":"includeEvents","required":false,"in":"query","schema":{"type":"string"},"description":"Include run events in the response (e.g., \"true\" or \"false\")"}],"responses":{"200":{"description":"Run details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Run not found"}},"summary":"Get Run details","tags":["Run"]}}},"components":{"schemas":{"RunDto":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"repeatRunId":{"type":"string","description":"The UUID of the run that was repeated","format":"uuid"},"userId":{"type":"string","description":"The ID of the user who initiated the run"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"result":{"type":"object","description":"Result of the run execution"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for run input format","format":"json-schema"},"input":{"type":"object","description":"Input provided when the run was created"},"intent":{"type":"string","description":"The intent or goal for the run"},"context":{"type":"string","description":"Additional context that was provided during run creation"},"createdAt":{"type":"string","description":"Timestamp when the run was created","format":"date-time"},"updatedAt":{"type":"string","description":"Timestamp when the run was last updated","format":"date-time"}},"required":["runId","repeatKey","status","executionPlanId","events","intent","context","createdAt","updatedAt"]}}}}
```

## Abort a Run

> Aborts a running or queued run by its ID

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs/{runId}":{"delete":{"description":"Aborts a running or queued run by its ID","operationId":"RunController_abortRun","parameters":[{"name":"runId","required":true,"in":"path","description":"Unique identifier of the run to abort","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Run successfully aborted"},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Run not found"}},"summary":"Abort a Run","tags":["Run"]}}}}
```

## Server-Sent Events stream for a Run

> Establishes a Server-Sent Events connection to stream real-time updates for a run

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/runs/{runId}/sse":{"get":{"description":"Establishes a Server-Sent Events connection to stream real-time updates for a run","operationId":"RunController_sse","parameters":[{"name":"runId","required":true,"in":"path","description":"Unique identifier of the run to stream events for","schema":{"format":"uuid","type":"string"}},{"name":"includeToolCalls","required":false,"in":"query","schema":{"type":"string"},"description":"Include tool call details in the stream (e.g., \"true\" or \"false\"). By default, tool calls are not included and for step events, only text (description of the step) is included."}],"responses":{"200":{"description":"SSE stream established successfully","content":{"text/event-stream":{"schema":{"type":"string","description":"Server-sent events stream with run updates"}}}},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Run not found"},"500":{"description":"Internal server error or stream error"}},"summary":"Server-Sent Events stream for a Run","tags":["Run"]}}}}
```


# Execution Plan

## Get Execution Plan details

> Retrieves detailed information about a specific execution plan

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/execution-plans/{executionPlanId}":{"get":{"description":"Retrieves detailed information about a specific execution plan","operationId":"ExecutionPlanController_getExecutionPlan","parameters":[{"name":"executionPlanId","required":true,"in":"path","description":"Unique identifier of the execution plan to retrieve","schema":{"type":"string"}}],"responses":{"200":{"description":"Execution plan details retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecPlanResponseDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Execution plan not found"}},"summary":"Get Execution Plan details","tags":["ExecutionPlan"]}}},"components":{"schemas":{"ExecPlanResponseDto":{"type":"object","properties":{"planType":{"type":"string","description":"Plan serialization type","enum":["json","text"]},"plan":{"description":"Execution plan details","oneOf":[{"$ref":"#/components/schemas/ExecPlanDto"},{"type":"string"}]}},"required":["planType","plan"]},"ExecPlanDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional human-readable title for the execution plan"},"description":{"type":"string","description":"High-level description of the execution plan"},"steps":{"description":"Ordered list of steps to execute the plan","type":"array","items":{"$ref":"#/components/schemas/ExecPlanStepDto"}}},"required":["description","steps"]},"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```


# Feedback

## Get Feedback by Run ID

> Retrieve feedback for a specific run. Returns null if no feedback exists.

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/feedback/run/{runId}":{"get":{"description":"Retrieve feedback for a specific run. Returns null if no feedback exists.","operationId":"FeedbackController_getFeedbackByRunId","parameters":[{"name":"runId","required":true,"in":"path","description":"UUID of the run to get feedback for","schema":{"type":"string"}}],"responses":{"200":{"description":"Feedback retrieved successfully or null if not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponseDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Run not found"}},"summary":"Get Feedback by Run ID","tags":["Feedback"]}}},"components":{"schemas":{"FeedbackResponseDto":{"type":"object","properties":{"id":{"type":"string"},"runId":{"type":"string"},"rating":{"type":"string","enum":["positive","negative"]},"comment":{"type":"string"},"feedbackSource":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","runId","rating","createdAt","updatedAt"]},"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}},"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}}}}}
```

## Upsert Feedback

> Create or update feedback for a specific run

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/feedback/run/{runId}":{"put":{"description":"Create or update feedback for a specific run","operationId":"FeedbackController_upsertFeedback","parameters":[{"name":"runId","required":true,"in":"path","description":"UUID of the run to provide feedback for","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertFeedbackDto"}}}},"responses":{"200":{"description":"Feedback successfully created or updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponseDto"}}}},"400":{"description":"Invalid request body or parameters"},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Run not found"}},"summary":"Upsert Feedback","tags":["Feedback"]}}},"components":{"schemas":{"UpsertFeedbackDto":{"type":"object","properties":{"rating":{"type":"string","enum":["positive","negative"],"description":"Required when creating new feedback"},"comment":{"type":"string"},"source":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"}}},"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}},"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}},"FeedbackResponseDto":{"type":"object","properties":{"id":{"type":"string"},"runId":{"type":"string"},"rating":{"type":"string","enum":["positive","negative"]},"comment":{"type":"string"},"feedbackSource":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","runId","rating","createdAt","updatedAt"]}}}}
```

## Get Feedback by ID

> Retrieve feedback by its ID

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/feedback/{feedbackId}":{"get":{"description":"Retrieve feedback by its ID","operationId":"FeedbackController_getFeedback","parameters":[{"name":"feedbackId","required":true,"in":"path","description":"UUID of the feedback to retrieve","schema":{"type":"string"}}],"responses":{"200":{"description":"Feedback retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponseDto"}}}},"401":{"description":"Unauthorized - invalid API key"},"404":{"description":"Feedback not found"}},"summary":"Get Feedback by ID","tags":["Feedback"]}}},"components":{"schemas":{"FeedbackResponseDto":{"type":"object","properties":{"id":{"type":"string"},"runId":{"type":"string"},"rating":{"type":"string","enum":["positive","negative"]},"comment":{"type":"string"},"feedbackSource":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","runId","rating","createdAt","updatedAt"]},"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}},"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}}}}}
```


# User

## Revoke all tokens for a user

> Revokes all tokens associated with the specified user ID

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/users/{userId}/revoke-all":{"post":{"description":"Revokes all tokens associated with the specified user ID","operationId":"UserController_revokeAll","parameters":[{"name":"userId","required":true,"in":"path","description":"The user ID whose tokens will be revoked","schema":{"type":"string"}}],"responses":{"201":{"description":"Users tokens revoke triggered"},"401":{"description":"Unauthorized - invalid API key"}},"summary":"Revoke all tokens for a user","tags":["User"]}}}}
```


# Actions

## List actions

> Retrieves actions for the authenticated owner

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/actions":{"get":{"description":"Retrieves actions for the authenticated owner","operationId":"ActionsController_listActions","parameters":[],"responses":{"200":{"description":"Actions retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ListActionsResponseDto"}}}}},"401":{"description":"Unauthorized - invalid API key"}},"summary":"List actions","tags":["Actions"]}}},"components":{"schemas":{"ListActionsResponseDto":{"type":"object","properties":{"key":{"type":"string","description":"The key of the action"},"intent":{"type":"string","description":"The intent associated with the action"},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for action output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for action input format","format":"json-schema"},"useDefaultMapping":{"type":"boolean","description":"Whether default input/output mapping is used"},"updatedAt":{"format":"date-time","type":"string","description":"Action last update timestamp"},"createdAt":{"format":"date-time","type":"string","description":"Action creation timestamp"}},"required":["key","intent","useDefaultMapping","updatedAt","createdAt"]}}}}
```

## Run an action

> Runs an action for the specified action key, customer integration ID and user ID

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"servers":[{"url":"https://api.toolregistry.ai"}],"security":[{}],"paths":{"/v1/actions/run":{"post":{"description":"Runs an action for the specified action key, customer integration ID and user ID","operationId":"ActionsController_runAction","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunActionRequest"}}}},"responses":{"200":{"description":"Action run successfully. Returns runId and output in the shape of action's output schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunActionResponse"}}}},"401":{"description":"Unauthorized - invalid API key"}},"summary":"Run an action","tags":["Actions"]}}},"components":{"schemas":{"RunActionRequest":{"type":"object","properties":{"key":{"type":"string","description":"The key of the action to run"},"userId":{"type":"string","description":"The ID of the user who is running the action"},"input":{"type":"object","description":"Input parameters for the action. Required if action has input schema defined."},"passThrough":{"description":"Pass through data for the action run, e.g. credentials for the integrations used in the run","type":"array","items":{"type":"object"}}},"required":["key","userId"]},"RunActionResponse":{"type":"object","properties":{"runId":{"type":"string","description":"The ID of the run that was created","format":"uuid"},"output":{"type":"object","description":"The output of the action"}},"required":["runId","output"]}}}}
```


# Models

## The IntegrationResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"IntegrationResponseDto":{"type":"object","properties":{"integrationId":{"type":"string"},"customerIntegrationId":{"type":"string"},"active":{"type":"boolean"},"name":{"type":"string"},"description":{"type":"string"},"logoUrl":{"type":"string"},"host":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"},"authType":{"type":"string","enum":["NONE","API_TOKEN","OAUTH2","AWS_ASSUME_ROLE","GOOGLE_SERVICE_ACCOUNT_OAUTH"],"description":"Currently active authentication type for the integration."}},"required":["integrationId","customerIntegrationId","active","name","host","updatedAt","createdAt"]}}}}
```

## The IntegrationUserResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"IntegrationUserResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"active":{"type":"boolean"},"updatedAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"}},"required":["userId","active","updatedAt","createdAt"]}}}}
```

## The IntegrationUserAuthUrlRequestDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"IntegrationUserAuthUrlRequestDto":{"type":"object","properties":{"redirectUri":{"type":"string","description":"The optional redirect URI to redirect the user to after authentication. Askel will append query param ?status=success/error. If not provided, a default success/failure page will be shown."}}}}}}
```

## The IntegrationUserAuthUrlResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"IntegrationUserAuthUrlResponseDto":{"type":"object","properties":{"authorizeUrl":{"type":"string"}},"required":["authorizeUrl"]}}}}
```

## The RunDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"RunDto":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"repeatRunId":{"type":"string","description":"The UUID of the run that was repeated","format":"uuid"},"userId":{"type":"string","description":"The ID of the user who initiated the run"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"result":{"type":"object","description":"Result of the run execution"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for run input format","format":"json-schema"},"input":{"type":"object","description":"Input provided when the run was created"},"intent":{"type":"string","description":"The intent or goal for the run"},"context":{"type":"string","description":"Additional context that was provided during run creation"},"createdAt":{"type":"string","description":"Timestamp when the run was created","format":"date-time"},"updatedAt":{"type":"string","description":"Timestamp when the run was last updated","format":"date-time"}},"required":["runId","repeatKey","status","executionPlanId","events","intent","context","createdAt","updatedAt"]}}}}
```

## The PaginationDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"PaginationDto":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"pageSize":{"type":"number","description":"Number of runs requested per page"},"total":{"type":"number","description":"Total number of runs matching the query"}},"required":["page","pageSize","total"]}}}}
```

## The RunsWithPaginationDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"RunsWithPaginationDto":{"type":"object","properties":{"runs":{"description":"Runs matching the query","type":"array","items":{"$ref":"#/components/schemas/RunDto"}},"pagination":{"description":"Pagination metadata for the result set","allOf":[{"$ref":"#/components/schemas/PaginationDto"}]}},"required":["runs","pagination"]},"RunDto":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"repeatRunId":{"type":"string","description":"The UUID of the run that was repeated","format":"uuid"},"userId":{"type":"string","description":"The ID of the user who initiated the run"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"result":{"type":"object","description":"Result of the run execution"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for run input format","format":"json-schema"},"input":{"type":"object","description":"Input provided when the run was created"},"intent":{"type":"string","description":"The intent or goal for the run"},"context":{"type":"string","description":"Additional context that was provided during run creation"},"createdAt":{"type":"string","description":"Timestamp when the run was created","format":"date-time"},"updatedAt":{"type":"string","description":"Timestamp when the run was last updated","format":"date-time"}},"required":["runId","repeatKey","status","executionPlanId","events","intent","context","createdAt","updatedAt"]},"PaginationDto":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"pageSize":{"type":"number","description":"Number of runs requested per page"},"total":{"type":"number","description":"Total number of runs matching the query"}},"required":["page","pageSize","total"]}}}}
```

## The CustomerProvidedAwsCredentials object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CustomerProvidedAwsCredentials":{"type":"object","properties":{"accessKeyId":{"type":"string","description":"The AWS access key ID for authenticating requests."},"secretAccessKey":{"type":"string","description":"The AWS secret access key paired with the access key ID."},"sessionToken":{"type":"string","description":"The temporary session token for AWS STS credentials."}},"required":["accessKeyId","secretAccessKey","sessionToken"]}}}}
```

## The CustomerProvidedAwsAssumeRole object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CustomerProvidedAwsAssumeRole":{"type":"object","properties":{"roleArn":{"type":"string","description":"The ARN of the IAM role to assume for cross-account access."},"externalId":{"type":"string","description":"An external ID used to prevent confused deputy attacks when assuming the role."}},"required":["roleArn","externalId"]}}}}
```

## The CustomerProvidedOktaCredentials object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CustomerProvidedOktaCredentials":{"type":"object","properties":{"clientId":{"type":"string","description":"The Okta application client ID."},"kid":{"type":"string","description":"The key ID (kid) of the private key used to sign client assertions."},"privateKey":{"type":"string","description":"The PEM-encoded RSA private key used to sign client assertions (private_key_jwt)."},"oauthTokenUrl":{"type":"string","description":"The Okta OAuth 2.0 token endpoint URL for your organisation."},"scope":{"type":"string","description":"Space-separated list of Okta API scopes to request."},"host":{"type":"string","description":"Base host URL of your Okta organisation. If omitted, derived from oauthTokenUrl."}},"required":["clientId","kid","privateKey","oauthTokenUrl","scope"]}}}}
```

## The CustomerProvidedPassThrough object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CustomerProvidedPassThrough":{"type":"object","properties":{"customerIntegrationId":{"type":"string","description":"The ID of the customer integration this pass-through data applies to."},"headers":{"type":"object","description":"Custom headers to include in requests for this integration."},"query":{"type":"object","description":"Custom query parameters to include in requests for this integration."},"values":{"description":"Provider-specific auth credentials. Use one of: AWS temporary credentials, AWS assume-role, or Okta service app (private_key_jwt). Only one type may be supplied per integration.","oneOf":[{"$ref":"#/components/schemas/CustomerProvidedAwsCredentials"},{"$ref":"#/components/schemas/CustomerProvidedAwsAssumeRole"},{"$ref":"#/components/schemas/CustomerProvidedOktaCredentials"}]}},"required":["customerIntegrationId"]},"CustomerProvidedAwsCredentials":{"type":"object","properties":{"accessKeyId":{"type":"string","description":"The AWS access key ID for authenticating requests."},"secretAccessKey":{"type":"string","description":"The AWS secret access key paired with the access key ID."},"sessionToken":{"type":"string","description":"The temporary session token for AWS STS credentials."}},"required":["accessKeyId","secretAccessKey","sessionToken"]},"CustomerProvidedAwsAssumeRole":{"type":"object","properties":{"roleArn":{"type":"string","description":"The ARN of the IAM role to assume for cross-account access."},"externalId":{"type":"string","description":"An external ID used to prevent confused deputy attacks when assuming the role."}},"required":["roleArn","externalId"]},"CustomerProvidedOktaCredentials":{"type":"object","properties":{"clientId":{"type":"string","description":"The Okta application client ID."},"kid":{"type":"string","description":"The key ID (kid) of the private key used to sign client assertions."},"privateKey":{"type":"string","description":"The PEM-encoded RSA private key used to sign client assertions (private_key_jwt)."},"oauthTokenUrl":{"type":"string","description":"The Okta OAuth 2.0 token endpoint URL for your organisation."},"scope":{"type":"string","description":"Space-separated list of Okta API scopes to request."},"host":{"type":"string","description":"Base host URL of your Okta organisation. If omitted, derived from oauthTokenUrl."}},"required":["clientId","kid","privateKey","oauthTokenUrl","scope"]}}}}
```

## The CreateRunRequest object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CreateRunRequest":{"type":"object","properties":{"intent":{"type":"string","description":"The intent or goal for the run"},"selectedCustomerIntegrationIds":{"description":"Array of selected customer integration IDs to use for the run, or use [\"*\"] to include all active integrations","type":"array","items":{"type":"string"}},"context":{"type":"string","description":"Additional context for the run execution"},"userId":{"type":"string","description":"The ID of the user who is initiating the run"},"approvalStrategy":{"type":"string","description":"Approval strategy for the run","enum":["NONE","ON_TOOL_CALL","ON_START"]},"repeatKey":{"type":"string","description":"The key of the run to repeat","format":"string"},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for the run output format","format":"json-schema"},"passThrough":{"description":"Pass through data for the run, e.g. credentials for the integrations used in the run","type":"array","items":{"$ref":"#/components/schemas/CustomerProvidedPassThrough"}}},"required":["intent","selectedCustomerIntegrationIds","context","userId"]},"CustomerProvidedPassThrough":{"type":"object","properties":{"customerIntegrationId":{"type":"string","description":"The ID of the customer integration this pass-through data applies to."},"headers":{"type":"object","description":"Custom headers to include in requests for this integration."},"query":{"type":"object","description":"Custom query parameters to include in requests for this integration."},"values":{"description":"Provider-specific auth credentials. Use one of: AWS temporary credentials, AWS assume-role, or Okta service app (private_key_jwt). Only one type may be supplied per integration.","oneOf":[{"$ref":"#/components/schemas/CustomerProvidedAwsCredentials"},{"$ref":"#/components/schemas/CustomerProvidedAwsAssumeRole"},{"$ref":"#/components/schemas/CustomerProvidedOktaCredentials"}]}},"required":["customerIntegrationId"]},"CustomerProvidedAwsCredentials":{"type":"object","properties":{"accessKeyId":{"type":"string","description":"The AWS access key ID for authenticating requests."},"secretAccessKey":{"type":"string","description":"The AWS secret access key paired with the access key ID."},"sessionToken":{"type":"string","description":"The temporary session token for AWS STS credentials."}},"required":["accessKeyId","secretAccessKey","sessionToken"]},"CustomerProvidedAwsAssumeRole":{"type":"object","properties":{"roleArn":{"type":"string","description":"The ARN of the IAM role to assume for cross-account access."},"externalId":{"type":"string","description":"An external ID used to prevent confused deputy attacks when assuming the role."}},"required":["roleArn","externalId"]},"CustomerProvidedOktaCredentials":{"type":"object","properties":{"clientId":{"type":"string","description":"The Okta application client ID."},"kid":{"type":"string","description":"The key ID (kid) of the private key used to sign client assertions."},"privateKey":{"type":"string","description":"The PEM-encoded RSA private key used to sign client assertions (private_key_jwt)."},"oauthTokenUrl":{"type":"string","description":"The Okta OAuth 2.0 token endpoint URL for your organisation."},"scope":{"type":"string","description":"Space-separated list of Okta API scopes to request."},"host":{"type":"string","description":"Base host URL of your Okta organisation. If omitted, derived from oauthTokenUrl."}},"required":["clientId","kid","privateKey","oauthTokenUrl","scope"]}}}}
```

## The ExecPlanStepDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```

## The ExecPlanDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"ExecPlanDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional human-readable title for the execution plan"},"description":{"type":"string","description":"High-level description of the execution plan"},"steps":{"description":"Ordered list of steps to execute the plan","type":"array","items":{"$ref":"#/components/schemas/ExecPlanStepDto"}}},"required":["description","steps"]},"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```

## The ExecPlanResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"ExecPlanResponseDto":{"type":"object","properties":{"planType":{"type":"string","description":"Plan serialization type","enum":["json","text"]},"plan":{"description":"Execution plan details","oneOf":[{"$ref":"#/components/schemas/ExecPlanDto"},{"type":"string"}]}},"required":["planType","plan"]},"ExecPlanDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional human-readable title for the execution plan"},"description":{"type":"string","description":"High-level description of the execution plan"},"steps":{"description":"Ordered list of steps to execute the plan","type":"array","items":{"$ref":"#/components/schemas/ExecPlanStepDto"}}},"required":["description","steps"]},"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```

## The CreateRunResponse object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"CreateRunResponse":{"type":"object","properties":{"runId":{"type":"string","description":"Unique identifier for the created run","format":"uuid"},"repeatKey":{"type":"string","description":"Key passed or generated during creation to repeat the run later","format":"string"},"status":{"type":"string","description":"Current status of the run","enum":["QUEUED","RUNNING","AUTH_REQUIRED","PAUSED","CANCELED","NO_SUCCESS","PARTIAL_SUCCESS","SUCCESS","FAILED"]},"events":{"type":"array","description":"Array of events in the run","items":{"type":"object"}},"executionPlanId":{"type":"string","description":"The UUID of the execution plan that was used for the run","format":"uuid"},"plan":{"description":"Execution plan for the run","allOf":[{"$ref":"#/components/schemas/ExecPlanResponseDto"}]},"repeatRunId":{"type":"string","description":"The UUID of the run that is going to be repeated.","format":"uuid"}},"required":["runId","repeatKey","status","events","executionPlanId","plan"]},"ExecPlanResponseDto":{"type":"object","properties":{"planType":{"type":"string","description":"Plan serialization type","enum":["json","text"]},"plan":{"description":"Execution plan details","oneOf":[{"$ref":"#/components/schemas/ExecPlanDto"},{"type":"string"}]}},"required":["planType","plan"]},"ExecPlanDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional human-readable title for the execution plan"},"description":{"type":"string","description":"High-level description of the execution plan"},"steps":{"description":"Ordered list of steps to execute the plan","type":"array","items":{"$ref":"#/components/schemas/ExecPlanStepDto"}}},"required":["description","steps"]},"ExecPlanStepDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the step"},"description":{"type":"string","description":"Step description"},"dependsOnSteps":{"description":"List of step IDs that must be completed first","type":"array","items":{"type":"string"}}},"required":["id","description","dependsOnSteps"]}}}}
```

## The FeedbackSourceMetadataDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}}}}}
```

## The FeedbackSourceDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}}}}}
```

## The FeedbackMetadataDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}}}}}
```

## The UpsertFeedbackDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"UpsertFeedbackDto":{"type":"object","properties":{"rating":{"type":"string","enum":["positive","negative"],"description":"Required when creating new feedback"},"comment":{"type":"string"},"source":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"}}},"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}},"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}}}}}
```

## The FeedbackResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"FeedbackResponseDto":{"type":"object","properties":{"id":{"type":"string"},"runId":{"type":"string"},"rating":{"type":"string","enum":["positive","negative"]},"comment":{"type":"string"},"feedbackSource":{"$ref":"#/components/schemas/FeedbackSourceDto"},"metadata":{"$ref":"#/components/schemas/FeedbackMetadataDto"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","runId","rating","createdAt","updatedAt"]},"FeedbackSourceDto":{"type":"object","properties":{"type":{"type":"string","enum":["user","evaluator","api"]},"userId":{"type":"string"},"metadata":{"$ref":"#/components/schemas/FeedbackSourceMetadataDto"}}},"FeedbackSourceMetadataDto":{"type":"object","properties":{"device":{"type":"string"},"platform":{"type":"string"},"version":{"type":"string"},"sessionId":{"type":"string"},"userAgent":{"type":"string"}}},"FeedbackMetadataDto":{"type":"object","properties":{"version":{"type":"string"},"sessionId":{"type":"string"},"experimentId":{"type":"string"},"environment":{"type":"string"},"clientVersion":{"type":"string"}}}}}}
```

## The ListActionsResponseDto object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"ListActionsResponseDto":{"type":"object","properties":{"key":{"type":"string","description":"The key of the action"},"intent":{"type":"string","description":"The intent associated with the action"},"outputSchema":{"type":"object","description":"JSON Schema draft 7 schema for action output format","format":"json-schema"},"inputSchema":{"type":"object","description":"JSON Schema draft 7 schema for action input format","format":"json-schema"},"useDefaultMapping":{"type":"boolean","description":"Whether default input/output mapping is used"},"updatedAt":{"format":"date-time","type":"string","description":"Action last update timestamp"},"createdAt":{"format":"date-time","type":"string","description":"Action creation timestamp"}},"required":["key","intent","useDefaultMapping","updatedAt","createdAt"]}}}}
```

## The RunActionRequest object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"RunActionRequest":{"type":"object","properties":{"key":{"type":"string","description":"The key of the action to run"},"userId":{"type":"string","description":"The ID of the user who is running the action"},"input":{"type":"object","description":"Input parameters for the action. Required if action has input schema defined."},"passThrough":{"description":"Pass through data for the action run, e.g. credentials for the integrations used in the run","type":"array","items":{"type":"object"}}},"required":["key","userId"]}}}}
```

## The RunActionResponse object

```json
{"openapi":"3.0.0","info":{"title":"toolregistry.ai API","version":"1.0"},"components":{"schemas":{"RunActionResponse":{"type":"object","properties":{"runId":{"type":"string","description":"The ID of the run that was created","format":"uuid"},"output":{"type":"object","description":"The output of the action"}},"required":["runId","output"]}}}}
```


# Mental Model

### Mental model

1. **You describe an outcome** → `intent` (natural language).
2. **You attach guardrails** → `context`, `userId`, `selectedCustomerIntegrationIds`, `approvalStrategy`.
3. **The platform plans & executes** → builds a plan (text or JSON), calls tools from the allowed integrations, streams events via SSE, and returns a `result`.


# Core Objects

* **Run** - A single execution. Identified by `runId` (UUID). See lifecycle below. Response shape: `RunDto`.
* **Intent** - Declarative outcome. Keep it user-facing and testable (what, not how).
* **Integrations** - Tools the agent may call. Use tenant-scoped `customerIntegrationId`s when creating runs. `integrationId` is the global key.
* **Context** - Hidden execution parameters: timezone, filters, IDs, safety limits, feature flags. Do not expose to end users.
* **UserID** - Identity whose tokens/permissions apply (act-as-user). Treat as sensitive. Revoke tokens at the integration.
* **Approval Strategy** - `NONE`, `ON_START`, or `ON_TOOL_CALL` for human-in-the-loop control.
* **Plan** - Either `text` or `JSON`, included in `CreateRunResponse`. May be displayed for `ON_START` approvals.
* **Events (SSE)** - Real-time stream for status, step start/complete, approvals, errors, and final result.
* **Result** - Structured object (if produced) included in `RunDto.result`.
* **Feedback** - Thumbs-up/down and comments attached to a run for evaluation loops.


# Integrations

**Integrations** are the first thing you should set up as these are needed to create any runs.&#x20;

Manage them at <https://app.toolregistry.ai/integrations>&#x20;

* **Managed** - We host auth & connector logic; click-to-connect. Just toggle the switch and it will work.&#x20;
* **Custom** - Bring your own OpenAPI spec or register a remote MCP. Define auth and scopes, and keep specs focused on allowed actions.
* When creating runs, specify the integrations by passing `customerIntegrationIds` inside `selectedCustomerIntegrationIds`.&#x20;
  * This deterministically controls which tools are available for the run.
  * Use `["*"]` to allow all active integrations (be cautious in production).


# Integration instructions

Guidance for when/how to write additional instructions for integrations

#### Persistent guidance

Integration specific agent instructions are included in every AI run that uses that integration. Use it to fill in context the AI cannot discover on its own, or to correct behavior you've observed going wrong consistently.

#### When to use&#x20;

Only add instructions if you've experienced specific undesired behavior or the AI is missing context it consistently needs. Leave blank if things work as expected.

**What to put here**

* Domain terminology or entity relationships unique to this system (e.g. "user" in this API means a tenant; tenants belong to units, units belong to projects)
* Persistent defaults you always want applied regardless of the task (e.g. "default ticket status on creation is new")
* Account-specific structure the AI cannot discover from the API alone

**What not to put here**

* Task-specific instructions — those belong in the run intent or context
* Procedural steps for how to do things, unless that exact behavior is needed in every run without exception. Because these instructions are always present, procedure that conflicts with the current task or triggers unnecessary actions leads to worse results. Prefer describing what things are over how to handle them

#### Format

Plain text or simple markdown. One bullet or sentence per rule. Shorter is better.

**For managed integrations**

Global guidelines for this integration type are already present and applied automatically — you don't see them and your instructions don't replace them. What you write here is added on top, so focus on what is specific to your account or connection.


# Remote MCP integrations

If you add a new custom integration and define the `Remote MCP URL`, an MCP integration will be created.\
\
Next choose the right **API authentication configuration** - by default this is set as `OAUTH2`. You only need to change this if the remote MCP has api token auth or no auth. \
\
\## MCP Tool Discovery\
\
Tool discovery is based on the auth type and will be visible in the **MCP Tools** tab.\
\
`NONE` - if no authentication is required, the tools will be discovered once the authentication configuration is saved.\
\
`API_TOKEN` - tools will also be discovered upon saving the api key.

`OAUTH2` - since tool discovery requires OAuth authentication, then the discovered tools will only become visible once the first authentication has been successfully completed, either by pre-authing a user under **Integration Users** or by starting a run and finishing the OAuth consent flow.\
\
NB! For token refreshing to work, the correct token url must be configured under **API authentication configuration.** The default will be the same host that is used for the authorization flow with `/token` appended. But this can vary based on the MCP provider.


# Actions

**Actions** are the reusable building blocks you create to interact with third-party tools. They allow you to define exactly *what* you want to achieve and *how* the data should look when it returns to your system, regardless of the underlying API's complexity or user specific custom fields.

Once configured in the dashboard, Actions are invoked directly via our [API](/api-documentation/actions#post-v1-actions-run), providing a consistent interface for your application to communicate with external services.

### Intent & Normalization

The core power of an Action is **Data Normalization**. Instead of adapting your code to fit the unique response structures of dozens of different APIs, you define the output structure you need.

* [**Define the Intent**](/concepts/intent-and-context)**:** You describe the goal in natural language (e.g., "List all users" or "Get tickets from the 'Assets' project").
* [**Define the Schema**](/basics/structured-output)**:** You provide a JSON Schema representing the exact shape of the data you want to receive (output).
  * Optional input schema: Defining an input schema allows you to pass information to the action, e.g. `{ name: "John" }`  to search by name for an action "Find users by name"

The platform ensures that the data returned from the third-party service is transformed and mapped to fit your specific schema. This means your application always receives normalized, predictable data, whether it came from Google Workspace, Jira, or HubSpot.

### User Mappings

**User** **Mappings** define the integration(s) and executable logic that fulfills the Action.

You can mix and match mapping strategies within a single Action depending on the service:

#### 1. Default Mappings

For standardized services where the data structure is consistent across all organizations (such as **Google Workspace** or **Microsoft 365**), you can use a **Default Mapping**.

* **How it works:** You define the mapping logic once.
* **Benefit:** It automatically applies to every user that connects to that integration, requiring no per-user configuration.

#### 2. User-Specific Mappings

For highly customizable tools (such as **Jira** or **HubSpot**) where workflows vary between customers, the platform supports **User-Specific Mappings**.

* **How it works:** The system adapts the execution logic based on the specific user's configuration.
* **Example:** One customer might store IT assets in a Jira project named "IT," while another uses a specific label within a generic project.

**Interchangeability:** These strategies are interchangeable within a single Action. For example, your "List Users" Action can utilize a Default Mapping for your Google Workspace handling, while simultaneously using User-Specific Mappings for your HubSpot mappings to handle custom field variations.

### Execution Engine

Our execution model prioritizes reliability and performance by primarily executing code, and using AI agent based execution only when needed.

Each invocation of an action results in a [Run](/concepts/runs).

#### Agent Mode (Supervised)

When an Action is run for the first time or encounters unexpected errors, it operates in **Agent Mode**.

* **Role:** An AI Agent acts as an overseer.
* **Process:** The Agent supervises the execution of generated code step-by-step. It verifies that the API interaction is proceeding according to plan and can intervene dynamically if the code encounters unexpected behavior.

#### Code-Only Mode (Optimized)

Once the logic is established and verified, the Action automatically transitions to **Code-Only Mode**.

* **Role:** Pure code execution
* **Process:** The system executes code to achieve the intent without AI intervention for maximum speed.
* **Fallback:** If the code encounters an error (e.g., an API change or schema mismatch), the system automatically reverts to Agent Mode to diagnose the issue, heal the workflow, and update the code for future runs.


# Request & response at a glance

CreateRunRequest

```json
{
"intent": "Create a new contact in CRM from the latest inbound email",
"context": "timezone=Europe/Copenhagen (UTC+3); source=inbox; dedupe_by=email",
"userId": "user-123",
"approvalStrategy": "ON_TOOL_CALL",
"selectedCustomerIntegrationIds": ["*"]
}

```

CreateRunResponse

```json
{
"runId": "5f0b4fce-0b1e-4f4b-9f82-6273c5c4499a",
"status": "QUEUED",
"events": [],
"plan": {"planType":"text","plan":"Plan will be available shortly"}
}
```

RunDto (selected fields)

```json
{
"runId": "...",
"status": "QUEUED|RUNNING|AUTH_REQUIRED|PAUSED|CANCELED|SUCCESS|FAILED",
"stepsFinished": [ { /* step summaries */ } ],
"result": { /* outcome */ },
"createdAt": "2025-08-01T12:00:00Z",
"updatedAt": "2025-08-01T12:05:00Z"
}
```


# Lifecycle

```
QUEUED → RUNNING → (AUTH_REQUIRED | PAUSED)* → (SUCCESS | FAILED | CANCELED)
```

**Terminal states:** `SUCCESS`, `FAILED`, `CANCELED`

#### QUEUED

The run was accepted and is waiting to start (e.g., executor capacity, scheduling window).

* **What you can do:** Nothing required. You *can* abort queued runs.
* **Abort:** `DELETE /v1/runs/{runId}`.

***

#### RUNNING

The platform is planning/executing the steps and streaming events (SSE).

* **What you can do:** Observe via `GET /v1/runs/{runId}/sse`. Approvals or auth may pause progress.

***

#### AUTH\_REQUIRED

The run is blocked on user authorization (e.g., missing OAuth).

* **Where to get the link:** It’s provided in the SSE stream **and** under the events in the `RunDto` object.
* **What you should do:** Surface that auth URL to the user and resume once completed. The run returns to `RUNNING` automatically.

***

#### PAUSED

Execution is waiting for an approval decision.

* **Approve/deny:** `GET /v1/runs/approval?token=...` (one-time token).
* **Outcome:** Approve → back to `RUNNING`. Deny → typically `CANCELED`.

***

#### CANCELED

The run was aborted (via API) or denied during approval. **Final state.**

* **How to cancel:** Abort any running/queued run with `DELETE /v1/runs/{runId}`.
* **Next step:** Recreate a new run if you want to restart.

***

#### FAILED

The plan or a step couldn’t complete. **Final state.**

* **What to check:** HTTP status codes and event logs (tool errors, validation failures), `stepsFinished`, and the last event payload.
* **Next step:** Fix the cause (auth, approvals, payloads, rate limits) and create a fresh run.

***

#### SUCCESS

All planned steps completed. **Final state.**

* **What you can do:** Read `result` (if present) and any summary posted by your workflow (e.g., Slack/Email).


# Approvals

Approval strategies let **you control how much oversight you want** over what the ToolRegistry AI agent does.

* **Approval Strategies**
  * **NONE** — No prompts; executes immediately.
  * **ON\_START** — Approve the entire plan once before any tool calls.\
    Use this when you want a human end user to **review and confirm the plan** before execution begins. This gives oversight without interrupting each individual tool call.
  * **ON\_TOOL\_CALL** — Approve each external call (fine-grained). Since this can be noisy, it’s recommended to use only when you want your AI agent to approve every tool call.
* Use the one-time token link provided in the server-sent stream to give approvals:

```http
GET /v1/runs/approval?token=...
```


# Streaming

Subscribe to `GET /v1/runs/{runId}/sse` to observe behavior in real time.

Include optional query parameter `includeToolCalls`  if you want to have detailed request/response to be included for **step** event types.

### Event Format

```json
id: <sequential_int>
event: <event_name>
data: <json>
```

Special heartbeat:

```json
event: keepalive
data: null
```

A `keepalive` is emitted every 20 s while at least one client is connected.

### Data Events

<table><thead><tr><th width="151.92578125">Event</th><th>Description</th></tr></thead><tbody><tr><td><code>start</code></td><td>The run begins execution.</td></tr><tr><td><code>step</code></td><td>A step inside the run finishes. Can be used for giving updates to end user about how run is progressing.</td></tr><tr><td><code>authentication</code></td><td>The run needs user authentication (status transitions to <code>AUTH_REQUIRED</code>) or reports its outcome.</td></tr><tr><td><code>run_approval</code></td><td>The run requires manual approval before proceeding (Emitted if Create Run request has approval strategy set).</td></tr><tr><td><code>run_action</code></td><td>Records the approval decision made by the user (approved or denied).</td></tr><tr><td><code>status</code></td><td>Any other status change (e.g. <code>QUEUED</code>, <code>PAUSED</code>).</td></tr><tr><td><code>end</code></td><td>The run finishes (<code>SUCCESS</code>, <code>FAILED</code>, or <code>CANCELED</code>).</td></tr></tbody></table>

Every data event contains:

```
{
    runId: string
    runStatus: "QUEUED" | "RUNNING" | "SUCCESS" | "FAILED" | "CANCELED" | "AUTH_REQUIRED" | "PAUSED"
    timestamp: (ISO-8601 string)
}
```

### Payload reference

#### `start`

```
{
  "runId": string,
  "runStatus": "RUNNING",
  "timestamp": ISO-8601 string
}
```

#### `step`

```
{
  "runId": string,
  "runStatus": "RUNNING" | "PAUSED",
  "timestamp": ISO-8601 string,
  "step": {
    "reason": string,
    "approval": "APPROVED" | "DENIED" | "PENDING" | "NOT_REQUIRED",
    "approvalActions"?: [{
      "action": "approve" | "deny",
      "url": string
    }],
    "toolCalls"?: [{
      "toolCallId": string,
      "toolName": string,
      "toolParameters": object,
      "toolResult"?: {
          "approvalResult": { "status": string, "text": string },
          "executionResult": object
      }
    }],
    "text"?: string
  }
}
```

#### `authentication`

```
{
  "runId": string,
  "runStatus": "AUTH_REQUIRED" | "RUNNING" | "FAILED",
  "timestamp": ISO-8601 string,
  "auth":
    | { "status": "required", "url": string, "integrationName": string }
    | { "status": "success" }
    | { "status": "denied" }
}
```

#### `run_approval`

```
{
  "runId": string,
  "runStatus": "PAUSED",
  "timestamp": ISO-8601 string,
  "approvalActions": [{
      "action": "approve" | "deny",
      "url": string
    }]

}
```

#### `run_action`

```
{
  "runId": string,
  "runStatus": "RUNNING" | "CANCELED",
  "timestamp": ISO-8601 string,
  "action": "approved" | "denied"
}
```

#### `status` (queued / paused)

```
{
  "runId": string,
  "runStatus": "QUEUED" | "PAUSED",
  "timestamp": ISO-8601 string,
  "reason"?: string
}
```

#### `end`

```
{
  "runId": string,
  "runStatus": "SUCCESS" | "FAILED" | "CANCELED",
  "timestamp": ISO-8601 string,
  "result": {
    "text": string,
    "structuredOutput"?: (user provided output schema)
  }
}
```


# Structured Output

Creating a run allows you to pass a **JSON schema** to format the response as structured output.&#x20;

The schema must be a **strict subset** of JSON Schema draft-07:&#x20;

* the root type must be object
* combinators (oneOf, anyOf, allOf) are not supported
* $ref is limited to internal #/definitions/ entries (prefer fully-resolved schemas)
* additionalProperties defaults to false.
* Supported data types are: string, number, integer, boolean, null, object, and array
* Max depth of schema is 10 and up to 200 nodes total.

An example of a Create Run request body:

```json
{
    "intent": "Give me deals in 'Proposal Made' stage and activities added for those deals",
    "selectedCustomerIntegrationIds": ["*"],
    "context": "Pipedrive",
    "ownerId": "xx_150",
    "userId": "user_441",
    "outputSchema": {
        "$schema": "https://json-schema.org/draft-07/schema",
        "type": "object",
        "properties": {
            "deals": {
                "type": "array",
                "description": "List of deals found",
                "items": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "number",
                            "description": "deal id"
                        },
                        "value": {
                            "type": "number",
                            "description": "value of the deal"
                        },
                        "currency": {
                            "type": "string",
                            "description": "Currency of the deal value"
                        },
                        "activityId": {
                            "type": ["number", "null"],
                            "description": "ID of the acitivity assigned to the deal"
                        }
                    },
                    "required": [
                        "id",
                        "value",
                        "currency",
                        "activityId"
                    ]
                }
            },
            "activities": {
                "type": "array",
                "description": "List of all activities",
                "items": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "number",
                            "description": "activity id"
                        },
                        "name": {
                            "type": "string",
                            "description": "name of the activity"
                        },
                        "scheduledDateTime": {
                            "type": "string",
                            "description": "time when the activity takes place"
                        }
                    },
                    "required": [
                        "id",
                        "name",
                        "scheduledDateTime"
                    ]
                }
            }
        },
        "required": [
            "activities",
            "deals"
        ]
    }
}
```

#### Response

```json
{
  "deals": [
    {
      "id": 7,
      "value": 900000,
      "currency": "USD",
      "activityId": null
    },
    {
      "id": 8,
      "value": 112343,
      "currency": "USD",
      "activityId": 1592
    },
    {
      "id": 10,
      "value": 126666,
      "currency": "USD",
      "activityId": null
    },
    {
      "id": 18,
      "value": 213000,
      "currency": "USD",
      "activityId": null
    },
    {
      "id": 21,
      "value": 213000,
      "currency": "USD",
      "activityId": 1593
    },
    {
      "id": 41,
      "value": 95000,
      "currency": "USD",
      "activityId": 1594
    },
    {
      "id": 43,
      "value": 75000,
      "currency": "USD",
      "activityId": 1595
    }
  ],
  "activities": [
    {
      "id": 1592,
      "name": "Lunch meeting",
      "scheduledDateTime": "2025-10-02T12:00:00+00:00"
    },
    {
      "id": 1593,
      "name": "Lunch meeting",
      "scheduledDateTime": "2025-10-03T12:00:00+00:00"
    },
    {
      "id": 1594,
      "name": "Lunch meeting",
      "scheduledDateTime": "2025-10-06T12:00:00+00:00"
    },
    {
      "id": 1595,
      "name": "Lunch meeting",
      "scheduledDateTime": "2025-10-07T12:00:00+00:00"
    }
  ]
}
```


# Concepts


# Intent & Context

Use **Intent** to say *what* to accomplish. Use **Context** to supply *everything the AI needs to do it safely and correctly*.

### Intent

Describe the goal in natural language. Be direct.

> "Schedule a meeting with John next week"

> "Create a new contact in CRM from the last inbound email"

Good intents are outcome‑focused&#x20;

### Context

Add execution details the agent shouldn’t have to infer or expose to end users. This is a free‑form guidance. Use it to pass:

* Time constraints and windows (`timezone=Europe/Copenhagen`, `window=next_week`)
* Environmental facts and feature flags (`crm=hubspot`, `dry_run=false`)
* Data bindings and IDs the UI shouldn’t show
* Safety constraints / policies (max changes per run, rate limits)
* Additional guidance for edge cases

**Do NOT leak sensitive context to users.** Context is meant for the system/agent, not for display.


# Runs

**Runs** are single, bounded executions of an **Intent** under explicit guardrails (**Context**, **User ID**, **Integration Selection**, **Approval Strategy**). A run plans, calls tools, streams events, and produces a final state and optional result.

### Why Runs exist

* Provide a durable unit of work you can audit and reason about.
* Keep actions tenant - and user-scoped (tokens tied to `userId`).
* Enforce safety via approvals and allow-listed integrations per run.
* Offer live observability via SSE and a consistent state model.

### Anatomy of a Run

* **Identity**: `runId` (UUID)
* **Intent**: outcome you want (natural language)
* **Context**: hidden execution parameters (timezone, filters, IDs, flags)
* **User ID**: whose permissions/tokens apply (act-as-user)
* **Integration Selection**: `selectedCustomerIntegrationIds` allow-list or `"*"` (all active)
* **Approval Strategy**: `NONE` | `ON_START` | `ON_TOOL_CALL`
* **Plan**: `text` or `json` (returned in `CreateRunResponse`)
* **Events (SSE)**: realtime lifecycle & step logs
* **Result**: optional structured output on completion

### Lifecycle

QUEUED → RUNNING → (AUTH\_REQUIRED | PAUSED)\* → (SUCCESS | FAILED | CANCELED)

* **QUEUED** — accepted, waiting to start
* **RUNNING** — planning/executing; events stream over SSE
* **AUTH\_REQUIRED** — needs user authorization (OAuth/token); auth link appears in SSE and Run events
* **PAUSED** — waiting for approval decision (`GET /v1/runs/approval?token=...`)
* **SUCCESS/FAILED/CANCELED** — terminal states; inspect logs, `stepsFinished`, and `result`

### Planning vs Execution

* **Planner** builds a plan (text/json) from Intent & Context within the limits of selected integrations and approval strategy.
* **Executor** performs tool calls, enforces approvals, rate limits, and emits events.

### Approvals (human‑in‑the‑loop)

* `NONE` — fire-and-forget.
* `ON_START` — approve the plan before any calls.
* `ON_TOOL_CALL` — approve each external call (recommended for production writes).

### Identity & Scope

* `userId` defines which tokens are used. Choose least privilege.
* `selectedCustomerIntegrationIds` limits which tools are callable this run.

### Observability

* Subscribe to `GET /v1/runs/{runId}/sse` for: `status`, `step.started`, `step.completed`, `approval.required`, `approval.resolved`, `result`, `error`.
* Use `GET /v1/runs/{runId}` to fetch the latest state; `stepsFinished` summarizes work done.

### Cancellation & retries

* Abort queued/running runs: `DELETE /v1/runs/{runId}`.
* To retry after failure or cancellation, create a **new run** with corrected context/auth.

### Best practices

* Write **declarative** intents; avoid how-to steps.
* Put sensitive/technical details in **Context** (not user-visible).
* Start strict with `ON_TOOL_CALL`; relax as confidence grows.
* Prefer explicit allow-lists over `"*"` in production.
* Batch responsibly (limits in Context), and watch SSE during rollout.


# Integrations

### What are integrations?

An integration exposes a set of tools (endpoints or commands) the agent can call. Each integration is either a managed connector or a custom one you bring.


# Managed Integrations

### Managed integrations

We host the connector and auth. You click connect, your users OAuth, and you’re done.

* **Auth**: OAuth/API keys handled by ToolRegistry
* **Schema**: automatic discovery of objects/fields where supported
* **Control:** ToolRegistry manages for you what endpoints/tools are allowed and what not. You can see it


# Custom Integrations

**Bring anything with an API spec or MCP.**

* **OpenAPI**: upload a spec; the system generates callable tools.
* **Remote MCP**: register a remote MCP server and let agents call it.
* **Auth**: define OAuth/API key flows and store tokens securely in ToolRegistry.
* **Control**: endpoint‑level allow‑lists and approvals

**Best practices**

* Keep specs small and focused on the actions you actually want to allow.
* Keep in mind that MCP allow lists/block lists are probabilistic in nature and always best effort basis
* Version your specs; treat changes as deployments


# Endpoint Controls

Deterministic, policy-enforced controls that define exactly **which endpoints/tools are allowed** for a given integration. Controls apply **at plan time** and are **hard-enforced at execution**.

* **Allow-list only**: there is **no block list**. Endpoints are **disabled by default** until you explicitly enable them.
* **Precedence**: Deterministic allow-list **>** Agent Instructions **>** Intent/Context hints. (The last two are probabilistic guidance.)

### What “Enabled” vs “Disabled” means

* **Enabled** — The planner may include the endpoint; the executor will permit the call.
* **Disabled (default)** — The endpoint is excluded from plans. If a call is attempted at execution time, it is blocked with a policy error and surfaced in events/SSE.

> Deterministic policy is the **ground truth**. The agent cannot route around it.

### Where to configure (UI)

**Integration → Integration Endpoints** tab:

* **Import OpenAPI** (`.json`/`.yaml`) to populate the table.
* **Search path** to filter rows (e.g., `/PublicHolidays`).
* **Method filter** to narrow the list (All, GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS).
* **Active** checkbox per row enables that (method, path) pair.
* **Select All** to enable everything currently filtered (use sparingly).
* **Save** to persist. Changes take effect for newly created plans immediately; execution always re-checks policy.

**Columns**

* **Active** (checkbox), **Path** (e.g., `/PublicHolidaysByDate`), **Method** (e.g., `GET`), **Source** (e.g., `api`), **Description** (from spec if available), **Updated At** (last change/import).

### Deterministic vs probabilistic inputs (and why you care)

* **Deterministic (hard)** — This allow-list. Exact, enforced at both plan and execution.
* **Probabilistic (soft)**
  * **Intent & Context** — Hints like “use Pipedrive to schedule…”; helpful, not binding.
  * **Agent Instructions** — Strong steering (“prefer read-only”; “avoid deletes”), still non-binding.

Deterministic settings win every time. Use them to reduce search space (faster/cheaper) and to prevent unsafe actions.

### Operational guidance

* **Default-deny** naturally: endpoints are off until you turn them on.
* **Enable narrowly**: only the (method, path) pairs you truly need.
* **Writes need scrutiny**: for POST/PUT/PATCH/DELETE, enable only what’s required and run with `approvalStrategy: ON_TOOL_CALL` at **run level.**
* **Spec imports**: when you re-import an OpenAPI file, treat it like a deployment. Newly discovered endpoints should remain disabled until reviewed.
* **Naming**: the identity is `(method, path)`. If `GET /reports` is enabled, `POST /reports` is **not** enabled unless you check it explicitly.

### Examples

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2Fh2zMUcQXiU9bzzBa7HeA%2Fimage.png?alt=media&amp;token=cf4a0736-1c67-453b-b0be-c42b84e8dd69" alt=""><figcaption></figcaption></figure>

**Open Holidays**&#x20;

* ✅ `GET /Countries`, `GET /Languages`, `GET /PublicHolidays`, `GET /PublicHolidaysByDate`
* (Leave any `POST/PUT/DELETE` rows **unchecked** unless you truly need them.)

**Pipedrive (CRM)**

* ✅ `POST /activities`, `PUT /deals/{id}` (only if required)
* 🔲 `DELETE /deals/{id}` (keep disabled unless you have a strong, audited use case)
* Run destructive operations with `approvalStrategy: ON_TOOL_CALL`.

#### Illustrative configuration (conceptual)

```
allowed_endpoints:
  - GET /PublicHolidays
  - GET /PublicHolidaysByDate
  - POST /activities
  - PUT /deals/{id}
# there is no deny list; anything not listed remains disabled
```

### Planner & Executor Behavior

* **Plan stage** - The planner filters candidate tool calls to only those endpoints explicitly enabled in the allow-list. Disabled endpoints are ignored.
* **Execution stage** - The executor enforces the same allow-list. Only enabled endpoints can be invoked; any attempt to use a disabled one is blocked with a policy error.

### Troubleshooting

* **“The plan never picks endpoint X.”**\
  It isn’t enabled, or the **method** differs. Enable the exact (method, path) and Save.
* **“Run failed with a policy error.”**\
  A disabled endpoint was attempted. Check **Integration Endpoints** and re-enable if appropriate; then start a new run.
* **“Planning is slow or scattered.”**\
  Tighten the allow-list. Smaller surface → faster, more accurate planning.

### Best practices

* Keep your allow-list minimal and explicit.
* Treat changes like deployments (review → stage → roll out).
* For write operations, combine a narrow allow-list with `approvalStrategy: ON_TOOL_CALL` at run level.
* Use Agent Instructions and Context to guide the model, but rely on the allow-list for guarantees.


# Agent Instructions for Integrations

Declarative, per-integration guidance that tells the agent **when** to call this integration and **how** to use it safely. Keep it outcome‑focused, scope‑limited, and explicit about side effects.

### Why this matters

Agents discover tools dynamically. Clear instructions reduce hallucinated calls, prevent unsafe writes, and keep cost/performance predictable.

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2FQyHPxUsYfN3WfSaJ4C7r%2FScreenshot%202025-09-01%20at%2009.46.59.png?alt=media&amp;token=f95f9566-5e31-4530-bced-cfa1955b6ec9" alt=""><figcaption></figcaption></figure>

### How to write good instructions

* **Be declarative**: describe *what to accomplish* and *when to use this integration*, not step-by-step mechanics.
* **Constrain scope**: enumerate allowed capabilities and explicitly forbid risky actions.
* **Name preconditions**: required fields, auth, scopes, and object existence checks.
* **Define side effects**: what gets written/updated; link to approvals if destructive.


# Integration Selection

Select which connections the run may use.

* **All**: `selectedCustomerIntegrationIds = ["*"]` (all active at runtime)
* **Allow‑list**: explicit array of `customerIntegrationId`s
* **Per‑tenant**: `customerIntegrationId` is per tenant; don’t pass global `integrationId` here.

**Design pattern**: build UI as *capability selectors* (e.g., "Can write meetings to HubSpot"), then map to specific `customerIntegrationId`s server‑side.


# Act‑as‑User (userId)

**Purpose**\
`userId` binds a run to a specific identity. That identity’s OAuth/API keys are used for all tool calls for the given integration. This determines **which user’s permissions and data will be used when executing the run**. Choose the user whose perspective should be used for the operation.

**Behavior**

* **Reuse:** If you (or your AI Agent) provide a previously used `userId`, the stored OAuth tokens for that user are reused, and your agent can act on behalf of that user.
* **Least privilege:** Pick the user with the minimum required permissions.
* **Security:** Treat `userId` as sensitive. Don’t expose it unnecessarily in logs or UI.
* **Revocation:** Tokens are revoked at the integration level; once re-authorized, the same `userId` can be reused.
* **Audit trail:** Reusing the same `userId` across runs preserves a consistent audit trail.

**OAuth Flow**

* If a **new identity** is provided, or the user does not yet have the required OAuth scopes to fulfill the plan, an OAuth flow is triggered automatically to collect the missing scopes.
* During this process, the system will return **OAuth authentication links** in the **Server-Sent Events (SSE) stream**. Your client (or AI Agent) is responsible for handling these links and surfacing them to the user to complete authorization.
* Once the flow is completed, the tokens are stored and reused for subsequent runs tied to the same `userId`.

## OAuth Token Lifetime & Refresh

TL;DR: *“OAuth tokens are kept alive by toolregistry.ai until revoked. We keep tokens refreshed until the integration allows.”*

**What happens**\
Toolregistry.ai keeps OAuth tokens **alive and refreshed** for the selected `userId` until you revoke them or the integration stops honoring refresh.

**How it works**

* **Auto-refresh:** We rotate access tokens using the provider’s refresh token before expiry.
* **Until it can’t:** Refresh continues **until** one of the following:
  * You revoke access (see  [#revocation](#revocation "mention")).
  * The provider invalidates/rotates the refresh token (`invalid_grant`, etc.).
  * Required scopes change and re-consent is needed.
  * Tenant/policy disables offline access or long-lived refresh.
  * Consent is withdrawn or the account is disabled.

## Revocation

Tokens are revoked at the **integration level**. Once re-authorized, the same `userId` can be reused.

To revoke:

1. Navigate to **Integrations > Integration Users**.
2. Enter the exact `userId` you want to revoke to search box - it will reveal the user.
3. Confirm the action — access for that identity will be removed until re-authorization.

<figure><img src="https://2871986476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FF8EK6M9Sp3cWWNqnvMRL%2Fuploads%2FpZKCoce3qiuZarfw8cuj%2Fimage.png?alt=media&amp;token=23003478-90d7-48be-b17f-f99fcfc0f1f5" alt=""><figcaption></figcaption></figure>


# Repeat key

How to ensure more consistent results and behavior during a run?

When creating a new run, you can pass a custom value that can then later be used to repeat the actions in a future run. This is especially useful when using it programmatically where consistency and expected behavior is important.

When a **repeatKey** is passed and the **intent, input/output schema** is exactly the same as the already existing action for that userId, then it will be a repeat run. A repeat run means that it will use the code used previously in a run to execute in a more consistent and reliable way.

### Using it

Set `repeatKey` field when creating a new run to a meaningful value to yo&#x75;**.** \
Recommendation: something that reflects the intent, e.g. `get_next_calendar_events`&#x20;

Other fields like `intent` , `context` , `selected integrations`  must still be set.


# Pass through

Users are able to add custom headers and query parameters that will be passed through to the selected integration. \
This allows the user to have more control over the automation and also own the authentication.<br>

To see how to use this, check the Create Run request in the documentation.

{% embed url="<https://docs.toolregistry.ai/api-documentation/run#post-v1-runs>" %}


