# List activities for a company
Source: https://docs.metal.ai/api-reference/activities/list-activities-for-a-company
/api-reference/openapi.json get /v1/companies/{id}/activities
Returns activities linked to a company.
# Search activities
Source: https://docs.metal.ai/api-reference/activities/search-activities
/api-reference/openapi.json post /v1/activities/search
Returns activities ranked by relevance to a query.
# Create an activity
Source: https://docs.metal.ai/api-reference/activity/create-an-activity
/api-reference/openapi.json post /v1/activities
Creates a new activity.
# Delete an activity
Source: https://docs.metal.ai/api-reference/activity/delete-an-activity
/api-reference/openapi.json delete /v1/activities/{id}
Deletes an activity by id.
# Get an activity
Source: https://docs.metal.ai/api-reference/activity/get-an-activity
/api-reference/openapi.json get /v1/activities/{id}
Retrieves a single activity by id.
# List activities
Source: https://docs.metal.ai/api-reference/activity/list-activities
/api-reference/openapi.json get /v1/activities
Returns activities in your organization.
# Update an activity
Source: https://docs.metal.ai/api-reference/activity/update-an-activity
/api-reference/openapi.json put /v1/activities/{id}
Updates an existing activity.
# Create an API key
Source: https://docs.metal.ai/api-reference/api-keys/create-an-api-key
/api-reference/openapi.json post /v1/keys
Creates a new API key. The secret `key` is returned only once in this response, so store it securely.
# Delete an API key
Source: https://docs.metal.ai/api-reference/api-keys/delete-an-api-key
/api-reference/openapi.json delete /v1/keys/{key}
Revokes an API key immediately. The key stops working within a short cache window.
# Get current API key
Source: https://docs.metal.ai/api-reference/api-keys/get-current-api-key
/api-reference/openapi.json get /v1/keys/current
Returns metadata for the API key used to authenticate this request.
# List API keys
Source: https://docs.metal.ai/api-reference/api-keys/list-api-keys
/api-reference/openapi.json get /v1/keys
Returns metadata for every API key in your organization. Secret values are never returned after creation.
# Create a company
Source: https://docs.metal.ai/api-reference/companies/create-a-company
/api-reference/openapi.json post /v1/companies
Creates a company. Only `canonicalName` is required.
# Delete a company
Source: https://docs.metal.ai/api-reference/companies/delete-a-company
/api-reference/openapi.json delete /v1/companies/{id}
Deletes a company by id.
# Enrich a company
Source: https://docs.metal.ai/api-reference/companies/enrich-a-company
/api-reference/openapi.json post /v1/companies/{id}/enrich
Triggers asynchronous enrichment for a company. Metal gathers candidate values from your documents, providers, and the web, then ranks them into one value per attribute. Poll the company and inspect its `enrichment` field for values and citations.
# Get a company
Source: https://docs.metal.ai/api-reference/companies/get-a-company
/api-reference/openapi.json get /v1/companies/{id}
Retrieves a single company by id.
# Get a company by external id
Source: https://docs.metal.ai/api-reference/companies/get-a-company-by-external-id
/api-reference/openapi.json get /v1/companies/externalId={externalId}
Retrieves a company using your own external identifier.
# Get a company by external reference
Source: https://docs.metal.ai/api-reference/companies/get-a-company-by-external-reference
/api-reference/openapi.json get /v1/companies/externalReference={ref}
Retrieves a company by its external source reference.
# Get companies by ids
Source: https://docs.metal.ai/api-reference/companies/get-companies-by-ids
/api-reference/openapi.json get /v1/companies/ids={ids}
Retrieves multiple companies in a single request.
# List companies
Source: https://docs.metal.ai/api-reference/companies/list-companies
/api-reference/openapi.json get /v1/companies
Returns companies in your organization, newest first. Supports pagination and sorting.
# List company financial metrics
Source: https://docs.metal.ai/api-reference/companies/list-company-financial-metrics
/api-reference/openapi.json get /v1/companies/{id}/financial-metrics
Returns financial metrics extracted for a company.
# Search companies
Source: https://docs.metal.ai/api-reference/companies/search-companies
/api-reference/openapi.json post /v1/companies/search
Returns companies ranked by relevance to a query.
# Update a company
Source: https://docs.metal.ai/api-reference/companies/update-a-company
/api-reference/openapi.json put /v1/companies/{id}
Updates an existing company. Provide the fields you want to change.
# Batch resolve observations
Source: https://docs.metal.ai/api-reference/data/batch-resolve-observations
/api-reference/openapi.json post /v1/data/observations/batch
Resolves up to 32 observations in a single request.
# Get a metric series
Source: https://docs.metal.ai/api-reference/data/get-a-metric-series
/api-reference/openapi.json get /v1/data/series
Returns a time series of metric observations for a resource and key.
# Get an observation
Source: https://docs.metal.ai/api-reference/data/get-an-observation
/api-reference/openapi.json get /v1/data/observations
Resolves the best observation value for a resource and key.
# List available metrics
Source: https://docs.metal.ai/api-reference/data/list-available-metrics
/api-reference/openapi.json get /v1/data/metrics
Returns the metric keys available for a resource.
# Query a metric series
Source: https://docs.metal.ai/api-reference/data/query-a-metric-series
/api-reference/openapi.json post /v1/data/metric-series
Returns a metric time series with advanced period and candidate filters.
# Create a deal
Source: https://docs.metal.ai/api-reference/deals/create-a-deal
/api-reference/openapi.json post /v1/deals
Creates a deal. Provide a `name` and, typically, the target `companyId`.
# Delete a deal
Source: https://docs.metal.ai/api-reference/deals/delete-a-deal
/api-reference/openapi.json delete /v1/deals/{id}
Deletes a deal by id.
# Get a deal
Source: https://docs.metal.ai/api-reference/deals/get-a-deal
/api-reference/openapi.json get /v1/deals/{id}
Retrieves a single deal by id.
# Get a deal by external id
Source: https://docs.metal.ai/api-reference/deals/get-a-deal-by-external-id
/api-reference/openapi.json get /v1/deals/externalId={externalId}
Retrieves a deal using your own external identifier.
# Get deals by ids
Source: https://docs.metal.ai/api-reference/deals/get-deals-by-ids
/api-reference/openapi.json get /v1/deals/ids={ids}
Retrieves multiple deals in a single request.
# List deals
Source: https://docs.metal.ai/api-reference/deals/list-deals
/api-reference/openapi.json get /v1/deals
Returns deals in your organization, newest first.
# List deals for a company
Source: https://docs.metal.ai/api-reference/deals/list-deals-for-a-company
/api-reference/openapi.json get /v1/companies/{id}/deals
Returns deals associated with a company.
# Search deals
Source: https://docs.metal.ai/api-reference/deals/search-deals
/api-reference/openapi.json post /v1/deals/search
Returns deals ranked by relevance to a query.
# Update a deal
Source: https://docs.metal.ai/api-reference/deals/update-a-deal
/api-reference/openapi.json put /v1/deals/{id}
Updates an existing deal.
# Get documents by ids
Source: https://docs.metal.ai/api-reference/documents/get-documents-by-ids
/api-reference/openapi.json get /v1/documents/ids={ids}
Retrieves multiple documents in a single request.
# Search documents
Source: https://docs.metal.ai/api-reference/documents/search-documents
/api-reference/openapi.json post /v1/documents/search
Searches across the content of documents ingested into Metal, ranked by relevance.
# Create an enrichment
Source: https://docs.metal.ai/api-reference/enrichments/create-an-enrichment
/api-reference/openapi.json post /v1/enrichments
Records an enriched property value on a resource.
# Delete an enrichment
Source: https://docs.metal.ai/api-reference/enrichments/delete-an-enrichment
/api-reference/openapi.json delete /v1/enrichments/{id}
Deletes an enrichment value by id.
# Get enrichment for a property
Source: https://docs.metal.ai/api-reference/enrichments/get-enrichment-for-a-property
/api-reference/openapi.json get /v1/enrichments/resources/{resourceType}/{resourceId}/{property}
Returns the top enrichment value for a specific property on a resource.
# List enrichments for a resource
Source: https://docs.metal.ai/api-reference/enrichments/list-enrichments-for-a-resource
/api-reference/openapi.json get /v1/enrichments/resources/{resourceType}/{resourceId}
Returns all enrichment values for a resource.
# Create an industry
Source: https://docs.metal.ai/api-reference/industry/create-an-industry
/api-reference/openapi.json post /v1/industries
Creates a new industry.
# Delete an industry
Source: https://docs.metal.ai/api-reference/industry/delete-an-industry
/api-reference/openapi.json delete /v1/industries/{id}
Deletes an industry by id.
# Get an industry
Source: https://docs.metal.ai/api-reference/industry/get-an-industry
/api-reference/openapi.json get /v1/industries/{id}
Retrieves a single industry by id.
# List industries
Source: https://docs.metal.ai/api-reference/industry/list-industries
/api-reference/openapi.json get /v1/industries
Returns industries in your organization.
# Update an industry
Source: https://docs.metal.ai/api-reference/industry/update-an-industry
/api-reference/openapi.json put /v1/industries/{id}
Updates an existing industry.
# API reference
Source: https://docs.metal.ai/api-reference/introduction
Conventions, authentication, and structure of the Metal REST API.
The Metal API is a JSON REST API for working with your firm's context layer: companies, deals, people, documents, activities, enrichments, scores, screenings, lists, workflows, and more. This reference documents every endpoint with an interactive playground. Set your credentials once and send live requests from the docs.
## Base URL
```
https://api.metal.ai
```
## Conventions
All endpoints live under the `/v1` prefix.
Requests and responses use `application/json`.
Successful responses wrap their payload in a top-level `data` field.
Every request is automatically scoped to your API key's organization.
## Authentication
Send both API key headers on every request:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/companies \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
See [Authentication](/authentication) for how to create and manage keys.
## Identifiers
Resource IDs are 24-character hexadecimal strings (for example, `665f1c2a9b1e4a0012a3b4c5`). Companies, deals, and people can also be addressed by your own `externalId` or `externalReference`.
## Conventions reference
| Topic | Behavior |
| ---------------- | --------------------------------------------------------------------------------- |
| Success envelope | `{ "data": ... }` |
| List envelope | `{ "data": [ ... ] }` |
| Search envelope | `{ "data": [ ... ], "metadata": { ... } }` |
| Errors | `{ "error": "message" }`. See [Errors](/guides/errors). |
| Pagination | `page`, `limit`, `lt`, `sort`, `direction`. See [Pagination](/guides/pagination). |
| Rate limits | `429` with backoff. See [Rate limits](/guides/rate-limits). |
## API sections
The sidebar groups endpoints into sections by how you typically use them:
Companies, deals, people, documents, and activities — the primary objects in your pipeline.
Enrichments, scores, and screenings — AI-powered intelligence on your records.
Lists, tags, and your industry/sector/subsector taxonomy.
Run and monitor AI workflows.
Time-series metrics and observations via the data layer.
API keys, teams, and workspaces for your organization.
## Resources
Jump to a concept for background on how these resources fit together:
Create, list, and revoke API keys.
Manage and enrich companies.
Track opportunities through your pipeline.
Manage contacts and relationships.
Search ingested documents.
Organize and enrich resources in bulk.
Run and monitor AI workflows.
How Metal enriches company and deal data.
# Add an entry
Source: https://docs.metal.ai/api-reference/lists/add-an-entry
/api-reference/openapi.json post /v1/lists/{id}/entries
Adds a resource to a list as a new entry.
# Create a list
Source: https://docs.metal.ai/api-reference/lists/create-a-list
/api-reference/openapi.json post /v1/lists
Creates a new list.
# Delete a list
Source: https://docs.metal.ai/api-reference/lists/delete-a-list
/api-reference/openapi.json delete /v1/lists/{id}
Deletes a list by id.
# Delete a list entry
Source: https://docs.metal.ai/api-reference/lists/delete-a-list-entry
/api-reference/openapi.json delete /v1/lists/{id}/entries/{entryId}
Removes an entry from a list.
# Enrich a list
Source: https://docs.metal.ai/api-reference/lists/enrich-a-list
/api-reference/openapi.json post /v1/lists/{id}/enrich
Triggers enrichment for all entries in a list.
# Get a list
Source: https://docs.metal.ai/api-reference/lists/get-a-list
/api-reference/openapi.json get /v1/lists/{id}
Retrieves a single list by id, including its column configuration.
# Get a list entry
Source: https://docs.metal.ai/api-reference/lists/get-a-list-entry
/api-reference/openapi.json get /v1/lists/{id}/entries/{entryId}
Retrieves a single entry from a list.
# List entries
Source: https://docs.metal.ai/api-reference/lists/list-entries
/api-reference/openapi.json get /v1/lists/{id}/entries
Returns the entries (rows) in a list.
# List lists
Source: https://docs.metal.ai/api-reference/lists/list-lists
/api-reference/openapi.json get /v1/lists
Returns the lists in your organization.
# Update a list
Source: https://docs.metal.ai/api-reference/lists/update-a-list
/api-reference/openapi.json put /v1/lists/{id}
Updates a list's metadata.
# Update a list entry
Source: https://docs.metal.ai/api-reference/lists/update-a-list-entry
/api-reference/openapi.json put /v1/lists/{id}/entries/{entryId}
Updates an entry in a list.
# Create a person
Source: https://docs.metal.ai/api-reference/people/create-a-person
/api-reference/openapi.json post /v1/people
Creates a person. Provide at least a name; link to a company with `company`.
# Delete a person
Source: https://docs.metal.ai/api-reference/people/delete-a-person
/api-reference/openapi.json delete /v1/people/{id}
Deletes a person by id.
# Get a person
Source: https://docs.metal.ai/api-reference/people/get-a-person
/api-reference/openapi.json get /v1/people/{id}
Retrieves a single person by id.
# Get a person by external reference
Source: https://docs.metal.ai/api-reference/people/get-a-person-by-external-reference
/api-reference/openapi.json get /v1/people/externalReference={ref}
Retrieves a person by their external source reference.
# Get people by ids
Source: https://docs.metal.ai/api-reference/people/get-people-by-ids
/api-reference/openapi.json get /v1/people/ids={ids}
Retrieves multiple people in a single request.
# List people
Source: https://docs.metal.ai/api-reference/people/list-people
/api-reference/openapi.json get /v1/people
Returns people in your organization, newest first.
# List people for a company
Source: https://docs.metal.ai/api-reference/people/list-people-for-a-company
/api-reference/openapi.json get /v1/companies/{id}/people
Returns people linked to a company.
# Search people
Source: https://docs.metal.ai/api-reference/people/search-people
/api-reference/openapi.json post /v1/people/search
Returns people ranked by relevance to a query.
# Update a person
Source: https://docs.metal.ai/api-reference/people/update-a-person
/api-reference/openapi.json put /v1/people/{id}
Updates an existing person.
# Create a score
Source: https://docs.metal.ai/api-reference/scores/create-a-score
/api-reference/openapi.json post /v1/scores
Records a score against a scoring framework.
# Delete a score
Source: https://docs.metal.ai/api-reference/scores/delete-a-score
/api-reference/openapi.json delete /v1/scores/{id}
Deletes a score by id.
# Get a score
Source: https://docs.metal.ai/api-reference/scores/get-a-score
/api-reference/openapi.json get /v1/scores/{id}
Retrieves a single score by id.
# Get a scoring framework
Source: https://docs.metal.ai/api-reference/scores/get-a-scoring-framework
/api-reference/openapi.json get /v1/scoring-frameworks/{id}
Retrieves a scoring framework by id.
# List scores
Source: https://docs.metal.ai/api-reference/scores/list-scores
/api-reference/openapi.json get /v1/scores
Returns scores in your organization.
# List scores for a company
Source: https://docs.metal.ai/api-reference/scores/list-scores-for-a-company
/api-reference/openapi.json get /v1/companies/{id}/scores
Returns scores associated with a company.
# List scoring frameworks
Source: https://docs.metal.ai/api-reference/scores/list-scoring-frameworks
/api-reference/openapi.json get /v1/scoring-frameworks
Returns scoring frameworks in your organization.
# Create a screening
Source: https://docs.metal.ai/api-reference/screening/create-a-screening
/api-reference/openapi.json post /v1/screenings
Creates a new screening.
# Delete a screening
Source: https://docs.metal.ai/api-reference/screening/delete-a-screening
/api-reference/openapi.json delete /v1/screenings/{id}
Deletes a screening by id.
# Get a screening
Source: https://docs.metal.ai/api-reference/screening/get-a-screening
/api-reference/openapi.json get /v1/screenings/{id}
Retrieves a single screening by id.
# List screenings
Source: https://docs.metal.ai/api-reference/screening/list-screenings
/api-reference/openapi.json get /v1/screenings
Returns screenings in your organization.
# Update a screening
Source: https://docs.metal.ai/api-reference/screening/update-a-screening
/api-reference/openapi.json put /v1/screenings/{id}
Updates an existing screening.
# List screenings for a company
Source: https://docs.metal.ai/api-reference/screenings/list-screenings-for-a-company
/api-reference/openapi.json get /v1/companies/{id}/screenings
Returns screenings associated with a company.
# List screenings for a deal
Source: https://docs.metal.ai/api-reference/screenings/list-screenings-for-a-deal
/api-reference/openapi.json get /v1/deals/{id}/screenings
Returns screenings associated with a deal.
# Search screenings
Source: https://docs.metal.ai/api-reference/screenings/search-screenings
/api-reference/openapi.json post /v1/screenings/search
Returns screenings ranked by relevance to a query.
# Create a sector
Source: https://docs.metal.ai/api-reference/sector/create-a-sector
/api-reference/openapi.json post /v1/sectors
Creates a new sector.
# Delete a sector
Source: https://docs.metal.ai/api-reference/sector/delete-a-sector
/api-reference/openapi.json delete /v1/sectors/{id}
Deletes a sector by id.
# Get a sector
Source: https://docs.metal.ai/api-reference/sector/get-a-sector
/api-reference/openapi.json get /v1/sectors/{id}
Retrieves a single sector by id.
# List sectors
Source: https://docs.metal.ai/api-reference/sector/list-sectors
/api-reference/openapi.json get /v1/sectors
Returns sectors in your organization.
# Update a sector
Source: https://docs.metal.ai/api-reference/sector/update-a-sector
/api-reference/openapi.json put /v1/sectors/{id}
Updates an existing sector.
# Create a subsector
Source: https://docs.metal.ai/api-reference/subsector/create-a-subsector
/api-reference/openapi.json post /v1/subsectors
Creates a new subsector.
# Delete a subsector
Source: https://docs.metal.ai/api-reference/subsector/delete-a-subsector
/api-reference/openapi.json delete /v1/subsectors/{id}
Deletes a subsector by id.
# Get a subsector
Source: https://docs.metal.ai/api-reference/subsector/get-a-subsector
/api-reference/openapi.json get /v1/subsectors/{id}
Retrieves a single subsector by id.
# List subsectors
Source: https://docs.metal.ai/api-reference/subsector/list-subsectors
/api-reference/openapi.json get /v1/subsectors
Returns subsectors in your organization.
# Update a subsector
Source: https://docs.metal.ai/api-reference/subsector/update-a-subsector
/api-reference/openapi.json put /v1/subsectors/{id}
Updates an existing subsector.
# Create a tag
Source: https://docs.metal.ai/api-reference/tag/create-a-tag
/api-reference/openapi.json post /v1/tags
Creates a new tag.
# Delete a tag
Source: https://docs.metal.ai/api-reference/tag/delete-a-tag
/api-reference/openapi.json delete /v1/tags/{id}
Deletes a tag by id.
# Get a tag
Source: https://docs.metal.ai/api-reference/tag/get-a-tag
/api-reference/openapi.json get /v1/tags/{id}
Retrieves a single tag by id.
# List tags
Source: https://docs.metal.ai/api-reference/tag/list-tags
/api-reference/openapi.json get /v1/tags
Returns tags in your organization.
# Update a tag
Source: https://docs.metal.ai/api-reference/tag/update-a-tag
/api-reference/openapi.json put /v1/tags/{id}
Updates an existing tag.
# Create a team
Source: https://docs.metal.ai/api-reference/team/create-a-team
/api-reference/openapi.json post /v1/teams
Creates a new team.
# Delete a team
Source: https://docs.metal.ai/api-reference/team/delete-a-team
/api-reference/openapi.json delete /v1/teams/{id}
Deletes a team by id.
# Get a team
Source: https://docs.metal.ai/api-reference/team/get-a-team
/api-reference/openapi.json get /v1/teams/{id}
Retrieves a single team by id.
# List teams
Source: https://docs.metal.ai/api-reference/team/list-teams
/api-reference/openapi.json get /v1/teams
Returns teams in your organization.
# Update a team
Source: https://docs.metal.ai/api-reference/team/update-a-team
/api-reference/openapi.json put /v1/teams/{id}
Updates an existing team.
# List team members
Source: https://docs.metal.ai/api-reference/teams/list-team-members
/api-reference/openapi.json get /v1/teams/{id}/members
Returns users who are members of a team.
# Cancel a workflow run
Source: https://docs.metal.ai/api-reference/workflows/cancel-a-workflow-run
/api-reference/openapi.json post /v1/workflow-runs/{id}/cancel
Cancels a running or queued workflow run.
# Create a workflow
Source: https://docs.metal.ai/api-reference/workflows/create-a-workflow
/api-reference/openapi.json post /v1/workflows
Creates a new workflow definition.
# Delete a workflow
Source: https://docs.metal.ai/api-reference/workflows/delete-a-workflow
/api-reference/openapi.json delete /v1/workflows/{id}
Deletes a workflow by id.
# Get a workflow
Source: https://docs.metal.ai/api-reference/workflows/get-a-workflow
/api-reference/openapi.json get /v1/workflows/{id}
Retrieves a single workflow definition by id.
# Get a workflow run
Source: https://docs.metal.ai/api-reference/workflows/get-a-workflow-run
/api-reference/openapi.json get /v1/workflow-runs/{id}
Retrieves a single workflow run by id, including its status and output.
# List all workflow runs
Source: https://docs.metal.ai/api-reference/workflows/list-all-workflow-runs
/api-reference/openapi.json get /v1/workflow-runs
Returns workflow runs across your organization, newest first.
# List workflow runs
Source: https://docs.metal.ai/api-reference/workflows/list-workflow-runs
/api-reference/openapi.json get /v1/workflows/{id}/runs
Returns runs for a workflow, newest first.
# List workflows
Source: https://docs.metal.ai/api-reference/workflows/list-workflows
/api-reference/openapi.json get /v1/workflows
Returns the workflows in your organization.
# Retry a workflow run
Source: https://docs.metal.ai/api-reference/workflows/retry-a-workflow-run
/api-reference/openapi.json post /v1/workflow-runs/{id}/retry
Retries a failed workflow run.
# Trigger a workflow run
Source: https://docs.metal.ai/api-reference/workflows/trigger-a-workflow-run
/api-reference/openapi.json post /v1/workflows/{id}/runs
Starts a new run of the workflow with the provided input. Runs are asynchronous; poll the run to track completion.
# Update a workflow
Source: https://docs.metal.ai/api-reference/workflows/update-a-workflow
/api-reference/openapi.json put /v1/workflows/{id}
Updates a workflow definition.
# Create a workspace
Source: https://docs.metal.ai/api-reference/workspace/create-a-workspace
/api-reference/openapi.json post /v1/workspaces
Creates a new workspace.
# Delete a workspace
Source: https://docs.metal.ai/api-reference/workspace/delete-a-workspace
/api-reference/openapi.json delete /v1/workspaces/{id}
Deletes a workspace by id.
# Get a workspace
Source: https://docs.metal.ai/api-reference/workspace/get-a-workspace
/api-reference/openapi.json get /v1/workspaces/{id}
Retrieves a single workspace by id.
# List workspaces
Source: https://docs.metal.ai/api-reference/workspace/list-workspaces
/api-reference/openapi.json get /v1/workspaces
Returns workspaces in your organization.
# Update a workspace
Source: https://docs.metal.ai/api-reference/workspace/update-a-workspace
/api-reference/openapi.json put /v1/workspaces/{id}
Updates an existing workspace.
# Search workspaces
Source: https://docs.metal.ai/api-reference/workspaces/search-workspaces
/api-reference/openapi.json post /v1/workspaces/search
Returns workspaces ranked by relevance to a query.
# Authentication
Source: https://docs.metal.ai/authentication
Authenticate Metal API requests with API keys.
The Metal API uses API keys to authenticate requests. Each key belongs to a single organization, and every request is automatically scoped to that organization's data.
## API key credentials
An API key has two parts that are always sent together:
| Credential | Header | Description |
| ---------- | ------------------- | -------------------------------------------- |
| Client ID | `x-metal-client-id` | Public identifier for the key. |
| Secret key | `x-metal-api-key` | Secret value that authenticates the request. |
Send both headers on every request:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/companies \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
The secret key is returned only once, when the key is created. If you lose it, delete the key and create a new one. Never expose it in client-side code, browsers, or mobile apps. API keys are for server-to-server use only.
## Create a key in the app
Create your first API key from the Metal app. You need an admin role to manage keys.
In the Metal app, go to **Settings → Organization → API & MCP Access**.
In the **API Keys** card, click **Create API Key**, enter a descriptive **Name** (for example, "Production sync"), and click **Create**.
Metal shows the new key's **Client ID** (`x-metal-client-id`) and **API Key** (`x-metal-api-key`). Copy both and store them securely.
This is the only time the **API Key** secret is shown. If you lose it, delete the key and create a new one.
The number of keys you can create depends on your plan. If you hit the limit, delete unused keys before creating new ones.
## Manage keys with the API
Once you have a key, you can review and revoke keys programmatically with the [API keys endpoints](/api-reference/introduction):
* `GET /v1/keys` lists every key in your organization. Secrets are never returned again, only metadata such as `name` and `keyLastFour`.
* `DELETE /v1/keys/{key}` revokes a key immediately. Revoked keys stop working within a short cache window.
Keys are created in the Metal app, not through the API. Use the app to create a key, then manage its lifecycle here.
## Best practices
Create a distinct key for each integration or environment so you can rotate or revoke them independently.
Create a new key, deploy it, then delete the old one to rotate without downtime.
Keep keys in a secret manager or environment variables, never in source control.
Name keys after the integration that uses them so you can audit and revoke precisely.
## Errors
If credentials are missing or invalid, the API responds with `401 Unauthorized`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{ "error": "invalid api key or clientId" }
```
See [Errors](/guides/errors) for the full list of status codes and how to handle them.
# Data model
Source: https://docs.metal.ai/concepts/data-model
Companies, deals, people, and how Metal's core resources relate.
Metal's data model centers on a few core resources that reference each other. Understanding how they relate makes the API easier to use.
## Core resources
An entity your firm tracks: an operating company, investor, advisor, or service provider.
An opportunity in your pipeline, usually tied to a target company.
A contact or executive, optionally linked to a company.
## Identifiers
Every resource has a Metal-assigned `id` (a 24-character hex string). You can also attach your own identifiers so you can reconcile records with external systems:
| Field | Purpose |
| ------------------- | ---------------------------------------------------------------------- |
| `id` | Metal's canonical identifier. |
| `externalId` | A single external identifier you control. |
| `externalReference` | A structured reference to a source system (for example, a CRM record). |
Companies, deals, and people can be fetched by external identifier as well as by Metal `id`. For example:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# By Metal id
curl https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5 \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
# By your external id
curl "https://api.metal.ai/v1/companies/externalId=crm-12345" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
## Companies
A company is the most central resource. The only required field on creation is `canonicalName`.
| Field | Type | Description |
| --------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- |
| `canonicalName` | string | The company's primary name. **Required.** |
| `alternativeNames` | string\[] | Other names the company is known by. |
| `website` | string | Primary website URL. |
| `description` | string | Long-form description. |
| `shortDescription` | string | One-line summary. |
| `sector` / `subsector` / `industry` | string | Classification labels. |
| `location` | object | Headquarters location. |
| `companyType` | string | `operating`, `investor`, `lender`, `investment_bank`, `advisor`, `sponsor`, or `service_provider`. |
| `tags` | string\[] | Tag references. |
| `assignees` | reference\[] | Users or teams responsible for the company. |
| `custom` | object | Org-defined custom fields. |
| `employeeCount` | number | Headcount. **Enriched.** |
| `ownershipStatus` | string | Private, public, or sponsor-owned. **Enriched.** |
| `lastValuation` / `lastAmountRaised` / `totalMoneyRaised` | number | Financial metrics. **Enriched.** See [Financial metrics](/concepts/financial-metrics). |
| `enrichment` | object | Discovered values and citations behind enriched fields. See [Reconciliation & ranking](/concepts/reconciliation). |
Fields marked **Enriched** are populated by Metal from your documents, data providers, and the web, then [reconciled](/concepts/reconciliation) into a single value. The `enrichment` object exposes the alternatives and their sources for each one.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": {
"id": "665f1c2a9b1e4a0012a3b4c5",
"canonicalName": "Acme Industrials",
"website": "https://acme.example",
"sector": "Industrials",
"companyType": "operating",
"createdAt": "2026-06-01T12:00:00Z",
"updatedAt": "2026-06-02T09:30:00Z"
}
}
```
## Deals
A deal represents an opportunity. It usually points at a target company via `companyId`.
| Field | Type | Description |
| --------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Deal name. |
| `companyId` | string | The target company's `id`. |
| `status` | string | Internal lifecycle status: `ACTIVE`, `CLOSED`, or `PASSED_DEAD`. |
| `stage` | string | Pipeline stage, for example `SOURCED`, `SCREENED`, `DUE_DILIGENCE`, `IC_MEETING`. |
| `owners` | reference\[] | Users or teams who own the deal. |
| `sector` / `industry` | string | Classification labels. |
| `size` | number | Deal size. |
| `acquisitionType` | string | Derived, read-only. `add_on` if the deal has a platform company, otherwise `platform`. Returned on every deal response; cannot be set via the API. |
Metal models deal status and stage with two layers: a fixed set of internal values (used for logic) and your firm's own custom labels (shown in the app). The API returns both the internal `status`/`stage` and the external labels where configured.
## People
A person is a contact, often linked to a company through the `company` field.
| Field | Type | Description |
| ------------------------ | ------ | ---------------------- |
| `firstName` / `lastName` | string | The person's name. |
| `email` | string | Primary email. |
| `currentTitle` | string | Current role. |
| `currentCompanyName` | string | Current employer name. |
| `company` | string | Linked company `id`. |
| `linkedinUrl` | string | LinkedIn profile URL. |
| `location` | object | Location. |
## Relationships
```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
graph LR
Person -->|works at| Company
Deal -->|targets| Company
Deal -->|owned by| User
Document -->|attached to| Company
Document -->|attached to| Deal
```
* A **deal** references a target **company** through `companyId`.
* A **person** references their **company** through `company`.
* **Documents** are attached to companies and deals and become searchable once ingested.
## Reading collections
List endpoints (`GET /v1/companies`, `GET /v1/deals`, `GET /v1/people`) return arrays under `data` and support [pagination](/guides/pagination). For relevance-ranked retrieval, use the [search endpoints](/guides/search) instead.
# Enrichment
Source: https://docs.metal.ai/concepts/enrichment
How Metal enriches companies with firmographics, financial metrics, and signals from AI providers, web search, and your uploaded documents.
Enrichment fills in what your firm doesn't already know. Metal combines AI with data providers and your own documents to populate attributes such as firmographics, [financial metrics](/concepts/financial-metrics), headcount, and signals on the resources you track.
Every enriched attribute keeps its provenance. Metal collects candidate values from many sources, ranks and [reconciles](/concepts/reconciliation) them into a single best value, and shows you the citation behind each one.
## How enrichment works
When you enrich a company, Metal gathers data from multiple sources, ranks and reconciles conflicting values, and writes the result back to the record. Enrichment runs asynchronously: you trigger it, and the enriched values appear on the record once processing completes.
Call the enrich endpoint for a company.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5/enrich \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Metal queries providers and AI models, then reconciles the results into a single best value per attribute.
Fetch the company again to read the enriched fields, such as `sector`, `description`, financials, and timestamps like `lastEnrichedAt`.
Enrichment is asynchronous. After triggering it, poll the company record (or wait for a [webhook](/guides/webhooks) if configured) rather than expecting enriched values in the immediate response.
## Discovered values and citations
For each attribute, Metal keeps every value it has discovered — not just the one it displays. Each discovered value carries its **source**, a **confidence** score, and **when** it was found, so you can always trace a value back to where it came from.
Metal [reconciles and ranks](/concepts/reconciliation) these candidates into a single displayed value. You can override the choice by **pinning** a specific value, which takes precedence over future enrichment until you unpin it.
## Sources and what gets enriched
An admin controls which attributes Metal enriches and which sources it draws from under **Settings → Enrichment**. Sources are prioritized — Metal prefers higher-priority connections when it reconciles conflicting values.
Beyond third-party data and web search, Metal extracts values from your own documents — CIMs, financial statements, board decks, and more. An admin chooses which document types to extract from and can add instructions to guide each extraction under **Settings → Extraction**.
For each document type — built-in or custom — admins can also narrow **which** financial metrics Metal extracts from that type. Expanding a type reveals a **Metrics to extract** checklist of every supported metric. Leaving everything checked (the default) keeps the current behavior of extracting every supported metric. Unchecking a metric tells Metal to skip it for that document type; clearing every metric effectively disables extraction for the type. Changes take effect on the next run, including reruns of existing CIM screenings, which rebuild the extraction prompt from the updated selection.
## Enrichment timestamps
Companies carry timestamps that tell you the state of enrichment:
| Field | Description |
| ------------------------- | ------------------------------------------------ |
| `lastEnrichmentStartedAt` | When the most recent enrichment run began. |
| `lastEnrichedAt` | When enrichment last completed and wrote values. |
If `lastEnrichmentStartedAt` is newer than `lastEnrichedAt`, a run is in progress.
## Enriching in bulk
To enrich many resources at once, add them to a [list](/api-reference/introduction) and trigger enrichment at the list level. Lists are the spreadsheet-like surface for working with resources in bulk, including computing enriched columns across every entry.
## Best practices
Create a company with at least a `canonicalName` (and a `website` when you have one) before enriching for the best match.
Trigger enrichment and check back later; don't hold a request open waiting for results.
The more identifying detail you supply (website, location), the more accurate enrichment is.
Use lists to enrich large sets efficiently instead of looping one-by-one.
# Financial metrics
Source: https://docs.metal.ai/concepts/financial-metrics
The financial attributes Metal enriches on companies — valuation, revenue, and custom metrics — and how they're sourced and reconciled.
Beyond firmographics, Metal enriches the financial attributes your firm uses to size and screen opportunities — valuation, capital raised, revenue, and more. Like every [enriched](/concepts/enrichment) value, each metric is drawn from multiple sources and [reconciled](/concepts/reconciliation) into a single trusted figure with a citation behind it.
## Financial attributes
Metal can enrich a range of financial metrics on a company. Common ones include:
| Field | Description |
| ------------------ | --------------------------------------------------------- |
| `lastValuation` | The company's most recent known valuation. |
| `lastRoundDate` | The date of the most recent funding round. |
| `lastAmountRaised` | The amount raised in the most recent round. |
| `totalMoneyRaised` | Total capital raised to date. |
| `lastFinancials` | The most recent reported financials (such as revenue). |
| `ownershipStatus` | Whether the company is private, public, or sponsor-owned. |
| `employeeCount` | Headcount, used as a size proxy. |
Which attributes Metal enriches — and which sources it uses — is configured by an admin under **Settings → Enrichment**. The exact set available to your organization depends on that configuration.
## Custom financial metrics
In addition to the standard set above, admins can define **custom financial metrics** for your firm — figures like EBITDAX, ARR growth, or any other number your team tracks that isn't already built in. Metal extracts them from documents alongside the standard metrics.
Manage custom metrics under **Settings → Financial Metrics** (admin-only). Each definition has:
* **Name** — the canonical identifier (for example, `EBITDAX`). Immutable after creation.
* **Display name** — how the metric appears in the app (for example, "EBITDA X").
* **Unit** — one of `currency`, `percentage`, `count`, or `multiple`. Immutable after creation.
* **Synonyms** — other names documents may use for the same metric. Extraction matches these too. Synonyms can't overlap with built-in metrics or other custom metrics.
* **Description** — a short prompt that helps the extraction model find this metric's values. You can refine it manually or use **Enhance** to have Metal rewrite it.
You can edit the display name, synonyms, and description at any time. Deleting a definition removes it from future extractions but leaves captured values untouched. Re-create a metric with the same name and unit to reactivate it with its history intact.
### Where custom metrics appear
Once defined, custom metrics render generically wherever Metal shows financial metrics:
* **CIM and screening detail pages** — extracted values appear in the financials tables using the display name you configured. If no definition is found for a `custom:` key, the app falls back to a prettified label derived from the name.
* **Company and deal lists** — open the **Add column** picker and pick from the new **Custom Metrics** section to add a metric as a column. Custom metric columns support the same filtering and editing as built-in metric columns.
Some surfaces are still built-in-only for now: summary widgets on CIM overviews, metric columns in the CIM library list, charts, and CSV/DOCX exports do not include custom metrics yet. Use the detail pages and list columns to view and work with custom values.
## Where financial values come from
Financial metrics are sourced the same way as any enriched attribute, with documents playing an outsized role:
* **Your documents** — Metal extracts figures from CIMs, financial statements, Quality of Earnings reports, board decks, and other files. Admins choose which document types to extract from under **Settings → Extraction**.
* **Third-party data and web search** — used to fill gaps and corroborate document figures.
* **User input** — values your team enters or confirms.
Because the same metric often appears across several sources with different numbers, Metal [ranks and reconciles](/concepts/reconciliation) them into one displayed value — and keeps the alternatives, each with its source, confidence, and date.
## Reading financial metrics over the API
Financial attributes are returned on the company record as their reconciled displayed values. Trigger enrichment, then read the company once it completes:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Trigger enrichment
curl -X POST https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5/enrich \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
# Read the enriched company
curl https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5 \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Use `lastEnrichedAt` to confirm a value reflects the latest run. See [Enrichment](/concepts/enrichment#enrichment-timestamps) for how to tell when a run is in progress.
Supplying a `website` and `location` when you create a company improves the accuracy of financial enrichment by helping Metal match the right entity across sources.
# Platform overview
Source: https://docs.metal.ai/concepts/platform
How Metal structures a firm's knowledge into an AI-ready context layer.
Metal is the AI context layer for financial firms. It connects your systems of record to the AI tools you already use, turning everything your firm knows — deals, documents, and the decisions behind them — into structured, AI-ready context. The platform organizes that knowledge into a connected data model, layers AI-driven intelligence and provenance on top, and lets you automate research with workflows — so your AI reasons with your firm's judgment, not just its files.
## Organizations and scoping
Every account belongs to an **organization** (your firm). All data, including companies, deals, people, documents, and lists, is scoped to your organization. API keys inherit this scope automatically, so you never pass an organization ID in requests; the API derives it from your key.
This means two things:
* You can only read and write your own organization's data.
* You never have to filter by organization yourself.
## The building blocks
Operating companies, investors, advisors, and other entities your firm tracks.
Opportunities moving through your pipeline, with status and stage.
Contacts, executives, and relationships connected to companies.
Files ingested into Metal, parsed and made searchable.
Spreadsheet-like views that organize and enrich resources in bulk.
AI automations that screen, research, and produce work product.
## Intelligence layer
On top of the raw data, Metal adds an intelligence layer:
* **Enrichment** fills in attributes such as firmographics, financials, and signals from AI and data providers. See [Enrichment](/concepts/enrichment).
* **Search** retrieves the most relevant companies, deals, people, and documents using semantic and keyword matching. See [Search](/guides/search).
* **Workflows** chain steps together to automate multi-stage research. See [Workflows](/concepts/workflows).
## How the API fits in
The API is the programmatic surface for the same platform the Metal app uses. Common integration patterns:
Sync companies, deals, and people from your CRM or internal systems using create and update endpoints.
Trigger enrichment and run workflows to turn raw records into intelligence.
Search and read enriched records to power dashboards, reports, or downstream systems.
Every request is JSON over HTTPS, versioned under `/v1`, authenticated with an API key, and scoped to your organization.
# Reconciliation & ranking
Source: https://docs.metal.ai/concepts/reconciliation
How Metal ranks discovered values from many sources and reconciles them into one trusted value.
A single attribute — a company's subsector, revenue, or ownership status — is often reported differently across a CIM, a market report, a web source, and your CRM. Reconciliation is how Metal turns those conflicting inputs into one trusted value, without throwing away the alternatives. It's a core part of what makes [enriched](/concepts/enrichment) data dependable.
## Discovered values
Whenever Metal finds a value for a field, it records it as a **discovered value** with its provenance:
| Attribute | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------- |
| Value | The value itself. |
| Source | Where it came from — a document (for example, a CIM), a market report, web search, third-party data, or a user. |
| Confidence | How confident Metal is in the value, as a percentage. |
| Recency | When the value was discovered. |
A field keeps every discovered value, so nothing is lost when sources disagree.
## How ranking works
To choose the value it displays, Metal ranks the discovered values. Ranking weighs:
* **Source priority** — the order of sources configured under **Settings → Enrichment**. Higher-priority connections win ties.
* **Confidence** — values Metal is more confident in rank higher.
* **Recency** — more recent values are preferred when sources are otherwise comparable.
* **User input** — a value a person set or confirmed is treated as authoritative.
The top-ranked value becomes the **displayed value** — the one returned by the API and shown across the app.
## Pinning and overrides
When you want a specific value regardless of ranking, **pin** it. A pinned value takes precedence over automatic reconciliation and stays put through future enrichment runs until you unpin it. This is how you correct or lock in a value your firm has verified.
## Why it matters
Each displayed value traces back to a source, a confidence score, and a date — so you can defend it in diligence.
Conflicting inputs are kept and ranked, not discarded, so you can see what every source said.
Pin verified values to override automation where your judgment should win.
Prioritize the connections your firm trusts, and reconciliation follows that order.
Over the API, each resource returns the reconciled displayed value on the attribute itself. See [Companies](/concepts/data-model#companies) and the [API reference](/api-reference/introduction) for the enriched fields available on each resource.
# Workflows, runs, and branch steps
Source: https://docs.metal.ai/concepts/workflows
Automate multi-step research with Metal AI workflows: branch steps, optional inputs, runs, retries, versioning, and webhook triggers.
Workflows are Metal's automation engine. A workflow chains together steps such as data lookups, AI reasoning, and document generation to automate research that would otherwise be manual. Each execution of a workflow is a **run**.
## Workflows and runs
* A **workflow** is the definition: the steps, inputs, and configuration.
* A **run** is a single execution of a workflow against specific inputs.
You manage definitions with the workflow endpoints and execute them by creating runs.
```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
graph LR
Workflow -->|create run| Run
Run -->|produces| Output
```
## Running a workflow
List your workflows to get the `id` of the one you want to run.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/workflows \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Trigger a run with the inputs the workflow expects.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/workflows/665f1c2a9b1e4a0012a3b4c5/runs \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "input": { "companyId": "665f1c2a9b1e4a0012a3b4c5" } }'
```
Poll the run to follow its progress and read its output when it completes.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/workflow-runs/665f1c2a9b1e4a0012a3b4c6 \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Runs are asynchronous. Creating a run returns immediately with a run record; poll `GET /v1/workflow-runs/{id}` (or use a webhook trigger) to track completion.
## Conditional branch steps
A **branch** step runs one of two arms based on a condition, so a workflow can take different paths at run time — for example, escalate to human approval only when a score is low, or skip a report step when an optional input was not provided.
* The condition is a single [CEL](https://github.com/google/cel-spec) expression that must evaluate to a boolean.
* The step has two arms — `then` and `else` — and each arm is an ordinary list of steps.
* Exactly one arm runs. Steps in the arm that did not run are recorded as **skipped**, and downstream references reaching into a skipped subtree render as empty rather than failing the run.
A condition can address:
* `input` — the run's inputs.
* `steps` — outputs of steps that ran strictly before the branch. It cannot see either arm's own children, since neither has run when the condition is evaluated.
* `triggeredBy` — the Metal user ID that started the run. It is empty when the run was system-triggered.
* `now` — the current time.
* Inside an iterator, `input.item` is the current work item and the branch is evaluated once per item. The separate `iterator` root carries frame metadata: `itemIndex`, `stepId`, `childStepId`, and `childStepIndex`.
Use `has(input.)` to test whether an optional input was supplied, and `"" in steps` to guard a reference to a step that may not have run.
```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
- type: branch
name: Escalate low-confidence matches
config:
condition: 'steps["score"].confidence < 0.7'
then:
- type: humanInLoop
name: Manual review
else:
- type: generateDocument
name: Auto-approve
```
### Nesting and validation
* Branches can nest inside their own arms, and inside iterators. Nesting is **capped at 3 branch levels deep**, counted from the workflow root — an iterator does not reset the count.
* Step IDs must be unique across the entire step tree, not just within one arm.
* A cross-arm reference — one arm reading a step in the other — is rejected at authoring time, because the two never both run. A reference made *after* the branch to a step inside an arm is allowed but flagged as conditional.
* Inside an iterator, arm children are restricted to inline-executable step types (`agent`, `completion`, `tool`, `generateDocument`, `executeCode`, and nested `branch`); `humanInLoop` and `iterator` are rejected inside a branch that itself sits in an iterator.
* Plain message-approval `humanInLoop` steps are allowed in a branch arm; structured-review approval is not.
## Workflow inputs
Each input in a workflow's schema is either **required** or **optional**:
* **Required** inputs must be supplied on every run. The Run form disables Run until each required field has a value, and names the specific fields that are still missing.
* **Optional** inputs may be left blank. A blank optional field is omitted from the run payload entirely rather than sent as `""` or `[]`, which matters for typed fields like resource IDs where an empty string is not a valid value.
* Inputs supplied by a trigger (webhook or event) and inputs meant for manual runs only are surfaced by the server's effective input schema and handled separately from author-supplied fields — you do not need to fill them in yourself when creating a run against a triggered workflow.
Presence is declared in the builder alongside each input's type, name, and default. A field whose default is blank reads as optional. A non-blank default keeps the field required, but the default fills it when omitted, so it never blocks a run.
When creating a run via the API, only include the fields you want to set; omit optional fields you want left unset.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"input": {
"companyId": "665f1c2a9b1e4a0012a3b4c6"
}
}
```
## Managing runs
Beyond creating and reading runs, you can:
* **Retry** a failed run: `POST /v1/workflow-runs/{id}/retry`
* **Re-run** with the same inputs: `POST /v1/workflow-runs/{id}/rerun`
* **Cancel** an in-progress run: `POST /v1/workflow-runs/{id}/cancel`
* **List** runs for a workflow: `GET /v1/workflows/{id}/runs`
## Versioning
Workflow definitions are versioned. You can list versions (`GET /v1/workflows/{id}/versions`) and restore a previous version (`POST /v1/workflows/{id}/versions/restore`) if a change needs to be rolled back.
## Triggering from external systems
Workflows can be triggered by inbound webhooks in addition to API calls. This lets an external system kick off research when an event happens on its side. See [Webhooks](/guides/webhooks) for how to configure and call a workflow's webhook trigger.
Build and edit workflow definitions in the Metal app, then use the API to trigger and monitor runs programmatically. This keeps complex authoring visual while automation stays in code.
# Automate research with workflows
Source: https://docs.metal.ai/guides/automate-workflows
Run Metal AI workflows from the API, track runs, and trigger them from events.
Workflows are Metal's automation engine: they chain data lookups, AI reasoning, and document generation into repeatable research. You author workflows visually in the Metal app, then use the API to run and monitor them. Each execution is a **run**. For the underlying model, see the [workflows concept](/concepts/workflows).
## Find the workflow to run
List your workflows to get the `id` of the one you want to execute.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/workflows \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
## Create a run
Trigger a run with the inputs the workflow expects. Runs are asynchronous: this returns immediately with a run record.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/workflows/665f1c2a9b1e4a0012a3b4c5/runs \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "input": { "companyId": "665f1c2a9b1e4a0012a3b4c6" } }'
```
## Track a run to completion
Poll the run until it finishes, then read its output.
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import time
import requests
BASE = "https://api.metal.ai/v1"
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
"Content-Type": "application/json",
}
def run_workflow(workflow_id, input_payload, interval=10, timeout=600):
run = requests.post(
f"{BASE}/workflows/{workflow_id}/runs",
headers=headers,
json={"input": input_payload},
).json()["data"]
terminal = {"completed", "failed", "cancelled"}
deadline = time.time() + timeout
while time.time() < deadline:
run = requests.get(f"{BASE}/workflow-runs/{run['id']}", headers=headers).json()["data"]
if run["status"].lower() in terminal:
return run
time.sleep(interval)
raise TimeoutError(f"Run {run['id']} did not finish in {timeout}s")
```
Prefer events over polling for long-running workflows. A [webhook](/guides/webhooks) trigger lets an external system both start a run and be notified when it finishes.
## Run a workflow from MCP
The Metal MCP server exposes the same workflow run path to MCP-compatible assistants such as Cursor, Claude, and ChatGPT. Use it when you want an assistant to discover the workflow, inspect required inputs, launch a run, and read the output without writing API code.
The usual MCP sequence is:
1. `list_workflows` — find the workflow ID. For example, search for a comparable-company or comparable-deals workflow by name or description.
2. `get_workflow` — inspect `inputSchema` and `inputUiSchema` so you know which company, deal, document, folder, or other inputs the workflow expects.
3. `run_workflow` — start the run with an `input` object that matches the schema.
4. `get_workflow_run` — poll until the run reaches a terminal status, then read outputs, artifacts, citations, or failures.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "run_workflow",
"arguments": {
"workflowId": "665f1c2a9b1e4a0012a3b4c5",
"input": {
"companyId": "665f1c2a9b1e4a0012a3b4c6"
}
}
}
```
If the run enters a human review state, inspect the pending step with `get_workflow_run_step` and complete the review with `review_workflow_hitl_step` when appropriate.
MCP access uses the signed-in Metal user's permissions. If `list_workflows` does not return the workflow, confirm that the user can see and run it in the Metal app.
## Manage runs
| Action | Endpoint |
| --------------------------- | ------------------------------------ |
| List runs for a workflow | `GET /v1/workflows/{id}/runs` |
| Get a run | `GET /v1/workflow-runs/{id}` |
| Retry a failed run | `POST /v1/workflow-runs/{id}/retry` |
| Re-run with the same inputs | `POST /v1/workflow-runs/{id}/rerun` |
| Cancel an in-progress run | `POST /v1/workflow-runs/{id}/cancel` |
## Trigger from external events
Instead of calling the API yourself, you can expose a webhook trigger on a workflow so an external system starts a run when something happens on its side — a new deal in your CRM, a file landing in storage, or a form submission.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/webhooks/workflows/your-workflow-slug \
-H "Content-Type: application/json" \
-d '{ "companyId": "665f1c2a9b1e4a0012a3b4c6" }'
```
See [webhooks](/guides/webhooks) for configuring the trigger and securing its secret.
## Next steps
Screen an entire set of targets in one pass.
Start runs from your own systems without managing API keys.
# Enrich companies
Source: https://docs.metal.ai/guides/enrich-companies
Trigger AI enrichment, track its progress, and enrich records in bulk.
Enrichment fills in what you don't already know — firmographics, financials, headcount, and signals — by combining AI with data providers. This guide is the task-oriented companion to the [enrichment concept](/concepts/enrichment): how to trigger it, know when it's done, and run it across many records.
## Trigger enrichment for one company
Enrichment runs asynchronously. Triggering it returns immediately; the enriched values appear on the record once processing completes.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5/enrich \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Create a company with at least a `canonicalName` — and a `website` when you have one — before enriching. The more identifying detail you provide, the more accurate the result.
## Know when enrichment is done
Don't hold a request open waiting for results. Instead, poll the company and compare two timestamps:
| Field | Meaning |
| ------------------------- | ------------------------------------------------ |
| `lastEnrichmentStartedAt` | When the most recent run began. |
| `lastEnrichedAt` | When enrichment last completed and wrote values. |
A run is still in progress while `lastEnrichmentStartedAt` is newer than `lastEnrichedAt`.
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import time
import requests
BASE = "https://api.metal.ai/v1"
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
}
def wait_for_enrichment(company_id, timeout=300, interval=10):
requests.post(f"{BASE}/companies/{company_id}/enrich", headers=headers)
deadline = time.time() + timeout
while time.time() < deadline:
company = requests.get(f"{BASE}/companies/{company_id}", headers=headers).json()["data"]
started = company.get("lastEnrichmentStartedAt")
finished = company.get("lastEnrichedAt")
if finished and (not started or finished >= started):
return company
time.sleep(interval)
raise TimeoutError(f"Enrichment for {company_id} did not finish in {timeout}s")
```
Prefer not to poll? Configure a [webhook](/guides/webhooks) trigger so Metal notifies your system when enrichment completes.
## Enrich in bulk
Looping `enrich` one company at a time works, but it's slow and burns through [rate limits](/guides/rate-limits). To enrich many records efficiently, add them to a [list](/guides/manage-lists) and enrich at the list level — Metal computes enriched columns across every entry.
Add the companies you want to enrich to a list.
Trigger enrichment at the list level so it fans out across all entries.
Read the list entries to pull enriched values for every company at once.
## Next steps
Group records to enrich and analyze them in bulk.
Run a workflow that enriches and screens targets end to end.
# Errors
Source: https://docs.metal.ai/guides/errors
HTTP status codes, error response format, and how to handle failures.
The Metal API uses conventional HTTP status codes to signal success or failure. Codes in the `2xx` range indicate success, `4xx` indicate a problem with the request, and `5xx` indicate a server-side error.
## Response format
Successful responses wrap their payload in a `data` field:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{ "data": { "id": "665f1c2a9b1e4a0012a3b4c5", "canonicalName": "Acme Industrials" } }
```
Errors return a JSON body with an `error` message:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{ "error": "invalid api key or clientId" }
```
Some validation errors return additional detail describing the offending field.
## Status codes
| Status | Meaning | What to do |
| --------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------- |
| `200 OK` | Request succeeded. | Nothing. |
| `201 Created` | Resource created. | Read the new resource from `data`. |
| `400 Bad Request` | Malformed request or invalid body. | Check field names, types, and required fields. |
| `401 Unauthorized` | Missing or invalid credentials. | Verify your `x-metal-client-id` and `x-metal-api-key` headers. |
| `403 Forbidden` | Authenticated but not allowed. | Your key lacks permission for this resource or route. |
| `404 Not Found` | Resource does not exist in your organization. | Confirm the `id` and that it belongs to your org. |
| `422 Unprocessable Entity` | Request understood but cannot be processed (for example, a plan limit). | Resolve the limit or conflict described in the body. |
| `429 Too Many Requests` | Rate limit exceeded. | Back off and retry. See [Rate limits](/guides/rate-limits). |
| `500 Internal Server Error` | Something went wrong on Metal's side. | Retry with backoff; contact support if it persists. |
## Handling errors
Always check the status code before reading the body. Treat `429` and `5xx` as retryable with exponential backoff; treat `4xx` (other than `429`) as a problem to fix in your request.
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import time
import requests
def request_with_retry(method, url, headers, max_retries=5, **kwargs):
for attempt in range(max_retries):
res = requests.request(method, url, headers=headers, **kwargs)
if res.status_code < 400:
return res
if res.status_code == 429 or res.status_code >= 500:
time.sleep(2 ** attempt) # exponential backoff
continue
# Non-retryable client error
res.raise_for_status()
res.raise_for_status()
```
When contacting [support@metal.ai](mailto:support@metal.ai) about an error, include the request method, path, status code, and the full error body. This is the fastest way to get a resolution.
# Build and manage lists
Source: https://docs.metal.ai/guides/manage-lists
Group companies, deals, and people into lists to work with them in bulk.
Lists are the spreadsheet-like surface for working with resources in bulk. Use them to assemble a screening universe, track a themed set of targets, or enrich and analyze many records at once.
## Create a list
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/lists \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Industrial automation — Q3 screen" }'
```
The response returns the new list under `data`, including its `id`. Use that `id` to add and read entries.
## Add entries
Entries are the resources a list contains. Add companies, deals, or people by referencing their `id`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/lists/665f1c2a9b1e4a0012a3b4c5/entries \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "entries": [{ "companyId": "665f1c2a9b1e4a0012a3b4c6" }] }'
```
## Read entries
Fetch the entries in a list to read their current values, including any enriched columns. Entries are paginated like other collections.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.metal.ai/v1/lists/665f1c2a9b1e4a0012a3b4c5/entries?limit=100" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Lists can hold many entries. Page through them with `limit` and `lt` as described in [pagination](/guides/pagination).
## Manage the list
| Action | Endpoint |
| ---------------- | ----------------------------- |
| List all lists | `GET /v1/lists` |
| Get one list | `GET /v1/lists/{id}` |
| Rename or update | `PUT /v1/lists/{id}` |
| Delete | `DELETE /v1/lists/{id}` |
| Read entries | `GET /v1/lists/{id}/entries` |
| Add entries | `POST /v1/lists/{id}/entries` |
## A common workflow
[Search](/guides/search) for relevant companies and add the matches as entries.
Enrich at the list level so every entry gets firmographics and financials. See [enrich companies](/guides/enrich-companies).
Read entries to rank, filter, or push results back to your own system.
Lists pair naturally with [workflows](/guides/automate-workflows): run a workflow over a list to screen an entire set of targets in one pass.
# Pagination
Source: https://docs.metal.ai/guides/pagination
Page through large collections with page, limit, and cursor parameters.
List endpoints return results in pages. Use query parameters to control page size, navigate pages, and sort results.
## Parameters
| Parameter | Type | Default | Description |
| ----------- | ------- | ------- | -------------------------------------------------------- |
| `limit` | integer | `100` | Items per page. Clamped to the range 1 to 1000. |
| `page` | integer | `1` | Page number, starting at 1. Maximum 100. |
| `lt` | string | None | Cursor: return items whose `id` is less than this value. |
| `sort` | string | `id` | Field to sort by. |
| `direction` | string | `desc` | Sort direction: `asc` or `desc`. |
By default, results are sorted by `id` descending (newest first).
## Offset pagination
The simplest approach is page-based. Request successive pages until you receive fewer items than `limit`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# First page of 50 companies
curl "https://api.metal.ai/v1/companies?limit=50&page=1" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
# Second page
curl "https://api.metal.ai/v1/companies?limit=50&page=2" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Offset pagination is capped: `page` cannot exceed 100. To iterate beyond the first 100 pages, or to page reliably through a dataset that is changing, use cursor pagination instead.
## Cursor pagination
Cursor pagination uses the `lt` ("less than") parameter with the `id` of the last item you saw. Because results default to descending `id` order, passing the last `id` returns the next, older page. This is stable even as new records are created.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.metal.ai/v1/companies?limit=100" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Read the `id` of the final item in the `data` array.
Pass that `id` as `lt`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.metal.ai/v1/companies?limit=100<=665f1c2a9b1e4a0012a3b4c5" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
Continue until a page returns fewer than `limit` items.
## Example loop
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import requests
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
}
url = "https://api.metal.ai/v1/companies"
params = {"limit": 100}
companies = []
while True:
res = requests.get(url, headers=headers, params=params)
res.raise_for_status()
page = res.json()["data"]
if not page:
break
companies.extend(page)
params["lt"] = page[-1]["id"]
if len(page) < params["limit"]:
break
print(f"Fetched {len(companies)} companies")
```
## Sorting
Override the default order with `sort` and `direction`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.metal.ai/v1/companies?sort=canonicalName&direction=asc" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
When sorting by a field other than `_id`, prefer offset pagination. The `lt` cursor is based on `id` ordering and is most reliable with the default sort.
# Rate limits
Source: https://docs.metal.ai/guides/rate-limits
How Metal rate limits API requests and how to handle 429 responses.
To keep the platform fast and fair for everyone, the Metal API enforces rate limits. Limits are applied per organization and depend on your plan.
## What to expect
When you exceed your rate limit, the API responds with:
```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
HTTP/1.1 429 Too Many Requests
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{ "error": "rate limit exceeded" }
```
Requests are throttled at the organization level, so all keys belonging to your organization share the same budget.
## Handling 429 responses
Treat `429` as a signal to slow down, not as a failure. Retry the request after a short delay, increasing the delay on repeated `429`s (exponential backoff).
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import time
import requests
def get_with_backoff(url, headers, max_retries=5):
delay = 1
for _ in range(max_retries):
res = requests.get(url, headers=headers)
if res.status_code != 429:
return res
time.sleep(delay)
delay *= 2 # exponential backoff
return res
```
## Best practices
Double the wait time after each consecutive `429` to let the limit recover.
Randomize backoff slightly so retries from multiple workers don't align.
Use list-based bulk operations instead of many single-record calls.
Avoid re-fetching data that rarely changes within a short window.
If your integration consistently needs higher throughput, contact [support@metal.ai](mailto:support@metal.ai) to discuss limits for your plan.
# Search companies, deals, people, documents, and activities
Source: https://docs.metal.ai/guides/search
Search Metal resources with hybrid, fuzzy, and full-text queries, structured filters, sorting, and paginated results across every core endpoint.
Search returns the most relevant records for a query, ranked by relevance rather than recency. Use it when you want to find records by meaning, keyword, or structured filter, instead of paging through an entire collection.
Each core resource exposes a search endpoint that accepts a `POST` body:
| Resource | Endpoint |
| ---------- | ---------------------------- |
| Companies | `POST /v1/companies/search` |
| Deals | `POST /v1/deals/search` |
| People | `POST /v1/people/search` |
| Documents | `POST /v1/documents/search` |
| Activities | `POST /v1/activities/search` |
These endpoints share the same request body shape and the same `{ data, metadata }` response envelope. They differ in the fields and search modes they support, and in the resource type returned in `data`.
The API also exposes `POST /v1/funds/search`, `POST /v1/limited-partners/search`, and `POST /v1/fundraising-processes/search` with the same request body shape. This guide focuses on the core resource endpoints above.
## Making a search request
Send a JSON body with your text, filters, and sort. Pass pagination as query parameters. Results come back under `data`, with pagination details in `metadata`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.metal.ai/v1/companies/search?page=1&limit=20" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "industrial automation suppliers",
"filters": {
"and": [
{ "field": "company_sector", "operator": "eq", "value": "Industrials" }
]
},
"sort": [
{ "field": "company_createdAt", "order": "desc" }
]
}'
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": [
{
"id": "665f1c2a9b1e4a0012a3b4c5",
"canonicalName": "Acme Industrials",
"sector": "Industrials"
}
],
"metadata": {
"page": 1,
"limit": 20,
"totalCount": 134,
"totalPages": 7
}
}
```
## Request body
| Field | Type | Description |
| ---------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text` | string | Free-text query. Ranked against a resource's searchable text fields. |
| `filters` | object | Structured filters. Combine with `and` / `or` arrays of filter clauses. |
| `sort` | array | Ordered list of `{ "field": "", "order": "asc" \| "desc" }` entries. |
| `hybrid` | boolean | Blend semantic and keyword ranking for company search. People and activities ignore this flag. Deals ignore it when `text` is present, but must keep it `false` when `text` is empty. Document search enables hybrid automatically when `text` is present. |
| `fullText` | boolean | Enable word-level matching on endpoints that support it, including activity search. Document search ignores this flag because `text` already uses its hybrid query path. |
| `fuzzy` | boolean | Enable approximate text matching on supported keyword-search paths. Document search and company hybrid search ignore this flag; activity search uses it only when `fullText` is `false`. |
For deterministic, filter-only queries, leave `text` empty and `hybrid`, `fuzzy`, and `fullText` set to `false`.
### Filters
Filters are structured predicates over a resource's indexed fields. Each clause names a `field`, an `operator`, and a `value`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"filters": {
"and": [
{ "field": "activity_type", "operator": "eq", "value": "Meeting" },
{ "field": "activity_startedAt", "operator": "gte", "value": "2026-01-01T00:00:00Z" }
],
"or": [
{ "field": "activity_participantScale", "operator": "in", "value": ["one_on_one", "group"] }
]
}
}
```
Nest additional `and` / `or` objects inside a clause to build compound expressions.
### Operators
| Field kind | Operators | Value |
| ---------------------- | -------------------------------------------------- | -------------------------------------------------- |
| String | `eq`, `neq`, `match`, `in`, `nin` | String; `in` and `nin` take an array |
| String array | `eq`, `neq`, `match`, `in`, `nin` | `eq`, `neq`, and `match` test individual members |
| Date | `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin` | RFC3339 timestamp such as `"2026-01-01T00:00:00Z"` |
| String or string-array | `exists`, `missing` | Omit `value` |
`eq` on a string field is **whole-value equality**, not a substring or contains match. `{ "field": "activity_subject", "operator": "eq", "value": "diligence" }` matches only an activity whose subject is exactly `diligence`, and returns nothing when the word appears inside a longer subject.
For "mentions this word" queries, put the word in `text` and set `fullText: true`. Use `match` for regular expressions. `contains` is not a supported operator.
### Sort
Sorting is stable only when you pass an explicit `sort`. Without one, the search backend returns results in whatever order it produces, which can vary between calls to the same query.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"sort": [
{ "field": "activity_startedAt", "order": "desc" },
{ "field": "activity_id", "order": "asc" }
]
}
```
When paging through a large result set, a single sort field is rarely enough — rows that tie on the primary field can shift between pages. Add a stable secondary field such as the resource's `id` to break every tie, and keep the same `sort` on every page.
## Searching activities
`POST /v1/activities/search` returns meetings, calls, emails, and notes filtered server-side. Prefer it over paging through the per-resource activity feeds (for example `GET /v1/companies/{id}/activities`) whenever the question carries a type, date window, participant, or linked resource.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.metal.ai/v1/activities/search?page=1&limit=100" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": {
"and": [
{ "field": "activity_type", "operator": "eq", "value": "Meeting" },
{
"field": "activity_relatedResources",
"operator": "eq",
"value": "fund:665f1c2a9b1e4a0012a3b4c5"
},
{
"field": "activity_startedAt",
"operator": "gte",
"value": "2026-01-01T00:00:00Z"
}
]
},
"sort": [
{ "field": "activity_startedAt", "order": "desc" },
{ "field": "activity_id", "order": "asc" }
]
}'
```
A few activity-specific rules that trip up first calls:
* `activity_type` is title-cased with spaces. Common built-in values are `"Meeting"`, `"Phone Call"`, `"Expert Call"`, `"Email"`, `"Note"`, and `"Other"`; connected systems may store additional labels. Comparisons are exact, so `"meeting"` will not match `"Meeting"`.
* IR links and person links in `activity_relatedResources` use `:` tokens. Supported public linkage types are `fund`, `limited_partner`, `fundraising_process`, `deal_investor_participation`, and `person`. A fund link is `"fund:665f1c2a9b1e4a0012a3b4c5"`, not a bare ObjectID.
* Deal links use `activity_deals` with a bare deal ID, not `activity_relatedResources`.
* Date fields require a full RFC3339 timestamp such as `"2026-01-01T00:00:00Z"`. A bare date like `"2026-01-01"` fails at query time.
* Word-level subject matching belongs in `text` with `fullText: true`. An `activity_subject` `eq` filter matches the whole subject and silently returns nothing when used as a keyword search.
* `activity_relatedResources` is not a sortable field.
## Searching documents
Document search is the way to query the content your firm has ingested into Metal. A non-empty `text` value automatically uses the hybrid query path across parsed document text.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.metal.ai/v1/documents/search?page=1&limit=10" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "customer concentration risk" }'
```
## Search vs. list
When you want relevance or a structured filter: finding records that match a concept, phrase, keyword, or field predicate.
When you want completeness: iterating over every record in order. See [Pagination](/guides/pagination).
## Pagination in search
Pass `page` and `limit` as query-string parameters. Search responses include a `metadata` object with `page`, `limit`, `totalCount`, and `totalPages`.
`page` defaults to `1` and `limit` defaults to `100`. The supported maximum page size is `400` across these search endpoints. Deal and activity search clamp larger requests to `400`; company, people, and document search reject requests above `400`. The response's `metadata.limit` reports the applied page size, so always page using the metadata values rather than the values you sent.
Deep pagination is capped: `(page - 1) * limit` cannot exceed 10,000. To reach records beyond that offset, narrow the filters into smaller result sets rather than paging further.
### Paging through a full result set
Hold `limit`, the request body, and the explicit `sort` constant across pages, append each response's `data`, and stop once `metadata.page` reaches `metadata.totalPages`.
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import requests
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
"Content-Type": "application/json",
}
body = {
"filters": {
"and": [
{"field": "activity_type", "operator": "eq", "value": "Meeting"}
]
},
"sort": [
{"field": "activity_startedAt", "order": "desc"},
{"field": "activity_id", "order": "asc"}
]
}
page = 1
limit = 400
activities = []
while True:
res = requests.post(
"https://api.metal.ai/v1/activities/search",
headers=headers,
params={"page": page, "limit": limit},
json=body,
)
res.raise_for_status()
result = res.json()
activities.extend(result["data"])
metadata = result["metadata"]
if metadata["page"] >= metadata["totalPages"]:
break
next_page = metadata["page"] + 1
if (next_page - 1) * metadata["limit"] > 10_000:
raise RuntimeError(
"The result set exceeds the deep-pagination limit; "
"narrow the filters and retry."
)
page = next_page
print(f"Fetched {len(activities)} activities")
```
# Sync your data
Source: https://docs.metal.ai/guides/sync-data
Create and keep companies, deals, and people in sync with your own systems.
Most integrations start by pushing your firm's data into Metal: the companies you track, the deals in your pipeline, and the people you know. This guide shows how to create records and keep them current as your source systems change.
## Pick an identifier strategy first
Before you write any data, decide how you'll reconcile Metal records with your source system. Every resource has a Metal-assigned `id`, but you can attach your own identifiers so you never have to store a mapping table.
| Field | Use it for |
| ------------------- | ---------------------------------------------------------------------- |
| `externalId` | A single stable identifier you control (for example, a CRM record ID). |
| `externalReference` | A structured reference to a source system when one ID isn't enough. |
Set `externalId` on create. It lets you fetch, update, and de-duplicate records later without tracking Metal's `id` yourself. See the [data model](/concepts/data-model) for the full field reference.
## Create records
A company needs only a `canonicalName`. Supply a `website` when you have one — it dramatically improves [enrichment](/guides/enrich-companies) accuracy.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/companies \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"canonicalName": "Acme Industrials",
"website": "https://acme.example",
"externalId": "crm-12345"
}'
```
Deals point at a target company through `companyId`, and people link to a company through `company`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/deals \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme — Project Forge",
"companyId": "665f1c2a9b1e4a0012a3b4c5",
"externalId": "crm-deal-987"
}'
```
## Keep records in sync
When a record changes in your system, update it in Metal. You can address a record by Metal `id` or by the `externalId` you set on create.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Update by Metal id
curl -X PUT https://api.metal.ai/v1/companies/665f1c2a9b1e4a0012a3b4c5 \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "shortDescription": "Maker of factory automation hardware." }'
# Look up by your external id
curl "https://api.metal.ai/v1/companies/externalId=crm-12345" \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
## Build an upsert loop
For an ongoing sync, fetch by `externalId`, then create the record if it's missing or update it if it exists. This keeps the job idempotent — safe to re-run without creating duplicates.
```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import requests
BASE = "https://api.metal.ai/v1"
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
"Content-Type": "application/json",
}
def upsert_company(record):
ext = record["externalId"]
existing = requests.get(f"{BASE}/companies/externalId={ext}", headers=headers)
if existing.status_code == 200:
company_id = existing.json()["data"]["id"]
return requests.put(f"{BASE}/companies/{company_id}", headers=headers, json=record)
return requests.post(f"{BASE}/companies", headers=headers, json=record)
```
Sync jobs make many calls in a row. Handle [rate limits](/guides/rate-limits) with backoff, and check [errors](/guides/errors) before reading a response body.
## Next steps
Fill in firmographics and financials after creating records.
Group synced records to work with them in bulk.
# Trigger workflows with webhooks
Source: https://docs.metal.ai/guides/webhooks
Trigger Metal workflows from external systems with webhook triggers, including shared-secret and HMAC-SHA256 signature verification.
Webhooks let an external system trigger a Metal [workflow](/concepts/workflows) when something happens on its side. Instead of polling, your system makes a single `POST` to a workflow's webhook trigger, and Metal starts a run.
## How workflow webhooks work
Each workflow can be configured with a webhook trigger that exposes a unique URL with a per-workflow slug:
```
POST https://api.metal.ai/webhooks/workflows/{slug}
```
When your system posts to this URL, Metal verifies the request and starts a workflow run with the payload as input.
In the Metal app, configure the workflow with a webhook trigger. Metal generates the slug and a secret used to verify incoming requests.
Choose how the sender will authenticate:
* **Shared secret** (default) — the sender includes the secret verbatim in a header. Easiest to set up; use it when you control both ends.
* **HMAC-SHA256** — the sender signs the raw request body with the secret and sends the digest in a header. Use it when integrating with a provider that signs payloads (for example, GitHub's `X-Hub-Signature` or Fireflies).
You can also override the **Signature header** name. Leave it blank to use the scheme default (`X-Metal-Webhook-Secret` for shared secret, `X-Hub-Signature` for HMAC-SHA256).
`POST` your event payload to the workflow's webhook URL, signed according to the configured scheme.
Shared secret:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/webhooks/workflows/your-workflow-slug \
-H "Content-Type: application/json" \
-H "X-Metal-Webhook-Secret: $METAL_WEBHOOK_SECRET" \
-d '{ "companyId": "665f1c2a9b1e4a0012a3b4c5" }'
```
HMAC-SHA256:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
BODY='{ "companyId": "665f1c2a9b1e4a0012a3b4c5" }'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$METAL_WEBHOOK_SECRET" -hex | awk '{print $2}')"
curl -X POST https://api.metal.ai/webhooks/workflows/your-workflow-slug \
-H "Content-Type: application/json" \
-H "X-Hub-Signature: $SIG" \
-d "$BODY"
```
The incoming payload becomes the run's input, available as `webhook.payload`, with `webhook.headers` and `webhook.query` also exposed. Track the resulting run with `GET /v1/workflow-runs/{id}`.
## Securing webhook triggers
Each webhook trigger has a secret and a signature scheme that together determine how Metal authenticates inbound requests. Anyone who can reach the URL with a valid signature can start a run, so treat the slug and secret like credentials.
### Shared secret (default)
The sender includes the trigger's secret as-is in a header. Metal compares it against the stored secret using a constant-time check.
* Default header: `X-Metal-Webhook-Secret`
* `Authorization: Bearer ` is also accepted when the default header is in use.
* Set a custom **Signature header** on the trigger to require a different header name (for example, `X-My-Token`). When a custom header is set, the `Authorization` fallback is disabled.
### HMAC-SHA256
The sender computes `HMAC-SHA256(secret, raw_request_body)` and sends the digest in a header. Metal recomputes the digest from the received body and compares it in constant time. The body must be signed exactly as transmitted — any reserialization will invalidate the signature.
* Default header: `X-Hub-Signature`
* Expected value: `sha256=` (GitHub/Fireflies format). A bare hex digest without the `sha256=` prefix is also accepted.
* Override the header name on the trigger to match a sender that uses something else (for example, `X-Hub-Signature-256`).
#### Example: Fireflies
Fireflies posts events with an `X-Hub-Signature` header containing `sha256=` of the raw body, signed with the secret you configure on their side. Configure the Metal trigger with:
* **Signature scheme**: HMAC-SHA256
* **Signature header**: leave blank (defaults to `X-Hub-Signature`)
* **Secret**: paste the same secret into Fireflies's webhook settings
Requests Fireflies sends will then verify automatically.
### Rotating the secret
Rotate a trigger's secret with:
```
POST /v1/workflows/{id}/trigger/webhook-secret/rotate
```
After rotating, update the new value on the sender side.
Treat webhook URLs and their secrets like credentials. If a slug or secret is exposed, rotate the secret immediately.
## Choosing between webhooks and the API
When an external system should start a workflow in response to its own events, without managing API keys.
When your code already authenticates with Metal and you want to create runs explicitly with `POST /v1/workflows/{id}/runs`.
Metal also receives inbound webhooks from connected providers (file storage, CRM, and others) to keep data in sync. Those integrations are configured in the app and don't require any setup in your own code.
# Custom attributes
Source: https://docs.metal.ai/help/admin/custom-attributes
Define firm-specific fields Metal tracks on companies, deals, people, and activities, including field keys, types, and how edits behave.
Custom attributes are firm-defined fields Metal tracks alongside the built-in schema. Use them to capture the data your firm cares about that isn't in Metal's default model — a proprietary score, a source tag, a due-diligence flag — and have it show up on records the same way built-in fields do.
Custom attributes are admin-gated and live under **Firm Knowledge**. Open **Settings → Custom Attributes**.
## Which objects support custom attributes
Attributes are defined per object type. Metal supports four:
* **Companies**
* **Deals**
* **People**
* **Activities**
The settings page has a tab for each. Switching tabs shows the fields defined on that object type.
If a tab shows "Custom attributes for … aren't available on this environment yet," the backend hasn't rolled out that object type here. Try again later or contact [support@metal.ai](mailto:support@metal.ai).
## Anatomy of a field
Each custom attribute has:
| Property | Notes |
| ----------------------- | ----------------------------------------------------------------------------------- |
| **Field name** | The base name you type when creating the field, e.g. `acme`. |
| **Type** | One of `string`, `number`, `boolean`, `array`, or `object`. |
| **Field key** | The stored key. Metal appends a type suffix to your base name (see below). |
| **Display name** | What teammates see in the app, e.g. "Acme Score". |
| **Description** | Optional short explanation of what the field means. |
| **Rendering component** | Optional hint for how the app should render the value (for example, `date-picker`). |
| **Indexed** | Whether Metal indexes the field for filtering and search. |
### Field-key suffix convention
The stored key always ends with a suffix that matches the type. Metal builds the key for you from your base name and the type you pick:
| Type | Suffix | Example key (base `acme`) |
| --------- | ------ | ------------------------- |
| `string` | `Str` | `acmeStr` |
| `number` | `Num` | `acmeNum` |
| `boolean` | `Bool` | `acmeBool` |
| `array` | `Arr` | `acmeArr` |
| `object` | `Obj` | `acmeObj` |
Legacy `date` fields use the `Date` suffix. You can still edit an existing `date` field, but you cannot create a new one from the app.
The key preview updates as you type the base name in the create dialog.
Base names must be non-empty, cannot start with an underscore, and cannot contain whitespace or the characters `.`, `$`, or `/`. Keys are unique per object type (case-insensitive).
## Create a field
Go to **Settings → Custom Attributes** and pick the tab for the object type you want to extend.
Enter a **Field Name** (the base name — Metal appends the type suffix), pick a **Type**, and set a **Display Name**. Add an optional **Description** and **Rendering Component** if relevant, and toggle **Indexed** if you'll filter or search on the field.
Metal writes the field to the object's schema. New records — and existing records that get updated — can store values under the new key.
Nested definitions for `array` items and `object` fields aren't editable from the dialog. Create the field with type `array` or `object`, then manage the nested shape through the API.
## Edit a field
Click the pencil icon on a row to change the display name, description, rendering component, or indexed setting. The field key itself is fixed once created — except when you change the type.
### Changing the type is a re-key
Because the key must match the type suffix, changing a field's type means creating a new key and removing the old one. Metal walks you through this:
1. Metal generates the new key by swapping the suffix — e.g. `acmeStr` → `acmeNum` when you change `string` to `number`.
2. You confirm the change in the "Change field type?" dialog.
3. The new field is created first; the old key is then deleted.
If Metal cannot remove the old key, both fields remain until you delete the old one manually.
Values previously stored under the old key stay on individual records but no longer match the new field, so they stop appearing in the app. In practice, treat a type change as a fresh field — plan to re-populate values on records that need them.
A type change is not a data migration. Stored values are not converted; they're orphaned under the old key.
## Delete a field
Click the trash icon on a row to remove a custom attribute. Deleting a field removes it from the schema so it no longer appears in the app, but values previously written to individual records are left in place, orphaned. Re-creating a field with the same key surfaces those values again.
## Where custom attributes show up
Once defined, custom attributes behave like other fields on their object type — they can be read and written through the Metal API and referenced in workflows, enrichment, and dashboards. Indexed attributes are additionally available as filters and in search.
# Dashboards
Source: https://docs.metal.ai/help/admin/dashboards
Use and edit dashboards in Metal.
Dashboards organize the information your team reviews most often. You can use them on deal, pipeline, company, personal, and shared dashboard surfaces.
Admins can manage dashboard templates from **Settings** → **Dashboards**. Access depends on your role and your organization's configuration.
## Edit a dashboard
Dashboard editing has two separate modes:
* **Edit layout** changes how existing widgets appear. Use it to move, resize, or remove widgets.
* **Assistant-based widget authoring** adds new widgets. Open the assistant on the dashboard and ask it to add a widget, such as a chart, table, KPI, or custom view.
Clicking **Edit layout** does not show a separate **Add widgets** button. If you want a new widget, use the assistant instead.
## Add a widget
1. Open the dashboard you want to change.
2. Open the assistant. On dashboard editor pages, the assistant may open automatically.
3. Ask for the widget you want to add. Be specific about the metric, table, chart, or view.
4. Review any clarifying questions from the assistant.
5. Save the layout after the widget appears, if you also moved or resized widgets.
Custom widgets can depend on your organization's rollout and permissions. If the assistant cannot add the widget, or you do not see the dashboard editing surface, contact your Metal team.
## Update existing widgets
When you open **Edit layout**, you can rearrange supported dashboards and remove widgets that are not protected defaults. Some default widgets are managed by Metal and cannot be edited directly. If a default widget has an update available, use **Update default widgets**.
# Deal Profiles
Source: https://docs.metal.ai/help/admin/deal-profiles
Understand how Metal groups your firm's deal history and uses those groups for similar deals, similar companies, and benchmarking.
**Deal Profiles** are firm-level groupings of your deal history. Metal generates them from the structured data it has about your deals and target companies, then uses them to power benchmarking surfaces outside chat.
You can review profiles in **Settings** → **Deal Profiles**. This page is available to admins.
## Where similarity appears
Metal can surface comparable information in several places:
* **Similar Deals** widgets on deal dashboards and deal pages. These show other deals from your organization that match the current deal on shared profile signals.
* **Similarities** chips in deal tables. These explain why a deal was selected, such as matching subsector, sector, revenue scale, EBITDA scale, outcome, or vintage.
* **Similar Companies** on company profiles. This is a company-level view based on enriched company taxonomy, such as sector and subsector.
* **Comparable-deal workflows**, when your organization has one configured. These can produce a richer output card or report for a specific research process.
## How Metal finds similar deals
Metal starts with the current deal and its linked target company.
It looks at structured signals such as:
* Company taxonomy, including industry, sector, and subsector.
* Deal intelligence extracted from your firm's data.
* Financial scale, including revenue and EBITDA tiers when available.
* Deal status, outcome, and timing where those fields are known.
Metal then compares those signals against your organization's Deal Profiles. Deals in the same or closely related profile buckets become candidates for the **Similar Deals** surface.
The strongest profile matches are prioritized first. For example, a shared subsector is generally a stronger explanation than a broad sector match. If multiple signals overlap, Metal shows the most useful reasons as **Similarities** chips.
Similarity is scoped to your organization. Metal does not use another customer's data to find comparable deals.
## Similar deals vs. similar companies
**Similar Deals** compares deal records. It is best for understanding how your firm has evaluated similar opportunities before.
**Similar Companies** compares company records. It is best for finding other companies in the same sector or subsector, even when there is no deal history attached.
## Comparable-deal workflows
Some teams use a workflow for deeper comparable-company or comparable-deal analysis. A workflow can be configured to inspect selected documents, pull structured deal data, compare scoring or financial metrics, and generate an output report.
Use workflows when you need a repeatable research process or a formatted deliverable. Use the Similar Deals widget when you need a quick in-app benchmark against existing deal history.
## Improve match quality
Similarity quality depends on the data Metal has. To improve results:
* Link each deal to the correct target company.
* Keep company taxonomy fields populated and reviewed.
* Sync CRM fields that capture sector, subsector, revenue, EBITDA, stage, and outcome.
* Upload and process CIMs, teasers, IC memos, and other relevant deal documents.
* Ask an admin or Metal to review **Deal Profiles** if expected comps are missing.
## Related
Create deals, link target companies, and track pipeline status.
Manage company records and review enriched profiles.
Run workflows and review generated output.
Find admin settings, including Deal Profiles.
# Set up NDA Library
Source: https://docs.metal.ai/help/admin/nda-library
Turn your firm's NDA checklist or playbook into a repeatable NDA review and negotiation workflow with the NDA Policy Compiler and NDA Library.
NDA Library reviews incoming NDAs against your firm's approved positions, identifies deviations, and produces a redline. When a counterparty returns an edited version, it can review those changes and prepare the next round.
Your firm's **NDA checklist or playbook** describes its preferred positions, acceptable fallbacks, and escalation requirements. The **NDA Policy Compiler** workflow converts it into a structured **NDA policy** used by NDA Library.
Setup has four parts:
1. Run NDA Policy Compiler and review its interpretation notes.
2. Create and publish the compiled policy in NDA Library.
3. Configure the default review and negotiation workflows.
4. Run a sample NDA and negotiation round.
## Before you start
You need:
* An admin role in Metal.
* Your firm's NDA checklist or playbook in plain text.
* A sample NDA and any counterparty response for testing as `.docx` files. PDF and legacy `.doc` files are not accepted.
* Access to NDA Library.
The playbook should clearly describe preferred positions, acceptable fallbacks, and provisions that require rejection or escalation.
## 1. Compile the NDA policy
1. Open **Workflows** from the sidebar.
2. Find **NDA Policy Compiler** and click **Run**.
3. Paste your checklist or playbook into `evaluation_framework`.
4. Click **Run**.
5. Wait for the workflow to complete.
The output contains two important sections:
* **Interpretation Notes** explains assumptions the compiler made when the playbook was ambiguous or internally inconsistent.
* **Compiled Policy (Copy This)** contains the structured JSON used by NDA Library.
Read every interpretation note before creating the policy. Pay particular attention to:
* Positions inferred from unclear wording.
* Conflicting instructions resolved by the compiler.
* Suggested changes that would make the playbook more explicit.
* Terms treated as preferences rather than mandatory requirements.
If an assumption is incorrect, update the source checklist or playbook and run **NDA Policy Compiler** again.
Do not resolve ambiguity by editing the compiled JSON directly. Keep the source playbook as the source of truth so future compilations produce the intended policy.
Once the interpretation notes match your firm's intent, copy the complete JSON from **Compiled Policy (Copy This)**.
## 2. Create and publish the policy
1. Open **NDA Library** from the sidebar.
2. Open **Settings**.
3. Under **Default policy**, click **Create one**.
4. Enter a descriptive policy name. The name sets an immutable slug and cannot be renamed later.
5. Select the **JSON** tab.
6. Paste the JSON copied from **Compiled Policy (Copy This)**.
7. Review the compiled policy in the preview.
8. Click **Save & publish**.
Do not paste your checklist or playbook into the **Plain text** tab. Run **NDA Policy Compiler** from **Workflows** first, then paste its compiled output into the **JSON** tab.
**Create one** appears when your organization has no published policies. If a published policy already exists, select it under **Default policy**.
Only published policies appear under **Default policy**.
## 3. Configure the defaults
1. Open **NDA Library → Settings**.
2. Under **Default policy**, select the policy you published.
3. Under **Review workflow**, select **NDA Review**.
4. Under **Negotiation workflow**, select **NDA Negotiation Round**.
5. Click **Save changes**.
These defaults apply when an organization member creates an NDA.
The **Email intake** section sets up an inbound address on the review workflow. You do not need it for the manual test in this guide.
## 4. Run a test NDA
Test the complete setup before introducing it to the wider team.
1. Open **NDA Library**.
2. Click **New NDA**.
3. Upload or select a sample NDA.
4. Enter a name.
5. Optionally associate it with a company or deal.
6. Click **Create & run**.
The review starts automatically using the configured policy and review workflow.
When the review finishes:
* Review the issues detected against the policy.
* Confirm that expected deviations were identified.
* Review the proposed changes and generated redline.
* Confirm that the result reflects the positions approved in the playbook.
If the result exposes a policy problem, update the source playbook, run **NDA Policy Compiler** again, and publish the corrected policy.
## 5. Test a negotiation round
1. Open the test NDA.
2. Click **Upload counterparty response**.
3. Upload the counterparty's returned markup.
4. Click **Start round**.
5. Review the detected changes and proposed response.
The negotiation workflow compares the counterparty's version with the last version your firm sent and applies the same published policy.
## Who can use NDA Library
Admins configure the default policy and workflows. A policy's creator can also edit that policy.
Once the defaults are configured, any authenticated organization member can create and run an NDA review.
## Troubleshooting
### NDA Library is unavailable
Contact your Metal representative if **NDA Library** does not appear in the sidebar.
### NDA Policy Compiler is unavailable
Contact your Metal representative if **NDA Policy Compiler** does not appear under **Workflows**.
### Create one does not appear
The **Create one** link appears only when your organization has no published policies. If a policy is already published, select it under **Default policy**.
### The policy does not appear in NDA settings
Only published policies appear under **Default policy**. Return to the policy editor and click **Save & publish**.
### New NDA cannot be started
Confirm that:
* A default policy is configured.
* **NDA Review** is selected under **Review workflow**.
* You have access to a personal workspace for the uploaded document.
### The review does not reflect the playbook
Update the source checklist or playbook, run **NDA Policy Compiler** again, review the new interpretation notes, and publish the corrected policy instead of modifying the generated JSON directly.
## Related topics
Run workflows and review their output.
Find personal preferences and organization-wide configuration.
Learn what each Metal role can do.
# Onboarding your workspace
Source: https://docs.metal.ai/help/admin/onboarding
Set up Metal for your firm for the first time.
A checklist for admins setting up a new Metal workspace. Metal guides you through this from **Settings → Setup**.
## Open the setup checklist
Go to **Settings → Setup** (under the **Getting Started** group). The checklist walks you through connecting data and configuring how Metal classifies and scores deals.
## 1. Connect your data sources
Connect file storage and CRM so Metal works with the systems you already use. See [Integrations overview](/help/integrations/overview).
## 2. Add your team
Request accounts for your colleagues and group them into teams. See [Adding & managing users](/help/admin/users).
## 3. Configure your taxonomy
Set the deal stages, statuses, and labels that match your firm's process. See [Taxonomy](/help/admin/taxonomy).
You don't have to finish everything at once — the Setup checklist tracks what's left.
# Roles & permissions
Source: https://docs.metal.ai/help/admin/roles
The roles in Metal and what each can do.
A member's **role** determines what they can see and do. Roles are shown on the **Members** page for reference.
## Available roles
| Role | What they can do |
| ---------- | ------------------------------------------------------------------------------------------------------- |
| **Member** | Use the app day to day; personal account settings. Most firm-wide admin settings are hidden. |
| **Power** | Everything a Member can, plus access to **Scoring** configuration. |
| **Admin** | Full access to firm settings — integrations, taxonomy, members, teams, and more. Can publish workflows. |
| **Owner** | Same administrative access as Admin. |
There is no separate "viewer" role. Some internal capabilities are reserved for the Metal team.
## Changing a role
Roles aren't self-serve in the app today. To change someone's role, use **Request to add users** on **Settings → Members** to describe the change, or contact [support@metal.ai](mailto:support@metal.ai). See [Adding & managing users](/help/admin/users).
Follow least privilege: give people **Member** by default and reserve **Admin**/**Owner** for those who manage firm-wide configuration.
# Workspace settings
Source: https://docs.metal.ai/help/admin/settings
Find your way around Metal's settings — personal preferences, firm-wide configuration, integrations, taxonomy, and organization management.
Settings hold both your personal preferences and (for admins) firm-wide configuration. Open **Settings** from your profile menu in the bottom-left of the sidebar, or press ⌘ ,. Use **Back to app** to return, and **Search settings** to jump to a section.
## How settings are organized
| Group | What's here |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Getting Started** | **Setup** — the onboarding checklist for your firm. |
| **Account** | Your personal preferences: **Connections**, **Profile**, **Notifications**, **Memories**, **Metal MCP**, **Prompts**, **Skills**. |
| **Firm Guidance** | How the firm wants Metal to behave: **Automations**, **Chat Defaults**, **Scoring**, **Firm Prompts**, **Firm Skills**, **System Prompts**, **Default Dashboards**. |
| **Firm Knowledge** | Your firm's data and how it's organized: **Integrations**, **Enrichment**, **Extraction**, **Financial Metrics**, **Taxonomy**, **Custom Attributes**, **Topics**, **Deal Profiles**. |
| **Organization** | People and access: **Members**, **Teams**, **API & MCP Access**, **Usage**, **Feedback Inbox**. |
Account settings are available to everyone. Firm-wide settings (Firm Guidance, Firm Knowledge, most of Organization) require an **admin** role.
## Common tasks
Add teammates and manage teams.
Connect CRM and file storage.
Configure deal stages, statuses, and labels.
Add firm-defined fields on companies, deals, people, and activities.
Choose what Metal enriches automatically.
Define custom financial metrics for extraction.
See how Metal groups deal history for similar deals and benchmarking.
Edit dashboard layouts and add widgets with the assistant.
# Taxonomy: stages, statuses, and labels
Source: https://docs.metal.ai/help/admin/taxonomy
Configure the deal stages, fundraising stages, statuses, industries, sectors, and company tags Metal uses to classify your firm's data.
Taxonomy is where admins define the labels Metal uses to classify your deals and companies — including the stages and statuses that power your pipeline. Open **Settings → Taxonomy**.
## Deal stages and statuses
Your firm's own stage and status labels map to Metal's internal values, so the pipeline reflects your process.
Go to **Settings → Taxonomy**.
In the **Deal Stages** or **Deal Statuses** card, click **Add Stage** / **Add Status**. Enter the **External Label** your team sees and map it to a **Metal Stage** / **Metal Status**, then click **Create**.
Use **Reorder** to set the order they appear in your pipeline.
These labels are what you see as columns on the [Active Pipeline](/help/deals) and in deal filters.
## Fundraising stages and statuses
If [Investor Relations](/help/investor-relations) is enabled for your organization, the Taxonomy page also has **Fundraising Stages** and **Fundraising Statuses** cards alongside the deal ones. These are separate from deal status and stage — they classify **fundraising processes**, not deals — but they're managed the same way: add, reorder, and archive labels, each mapped to a Metal internal value.
## Other taxonomy
The same page lets you manage:
* **Deal Actions** — actions that appear when a deal enters a stage.
* **Industries**, **Sectors**, and **Subsectors**.
* **Tags** applied to companies.
* **Company Statuses**.
Taxonomy covers the labels Metal classifies data with. The deal and company field schemas themselves are managed by Metal — to request changes, use the feedback options in settings or contact [support@metal.ai](mailto:support@metal.ai).
# Adding & managing users
Source: https://docs.metal.ai/help/admin/users
Request new members, create placeholder users, and organize people into teams.
Admins manage who's in the workspace from **Settings → Members**. This page covers requesting new members, creating placeholder users, and grouping people into teams.
You need an **admin** role to manage members. Open **Settings** from your profile menu in the bottom-left of the sidebar (or press ⌘ ,), then go to **Members** under the **Organization** group. If you don't see it, ask an admin at your firm.
## Request new members
To add teammates who can sign in, send a request to the Metal team with their details.
Go to **Settings → Members**.
Click **Request to add users**. In the dialog, enter each person's full name, email, and the role they need.
Click **Send**. The Metal team provisions the accounts and the new members appear in your **Members** list once they're set up.
Need to change someone's role or remove an active member? Those aren't self-serve in the app yet — use **Request to add users** to describe the change, or contact [support@metal.ai](mailto:support@metal.ai).
## Create a placeholder user
Placeholder users are lightweight records without login access — useful for representing a person on deals or teams before they have an account. They can be promoted to full members later.
On **Settings → Members**, click **Create Placeholder User**.
In the **Add Placeholder User** dialog, enter a **Name** and **Email**.
Click **Create**. The placeholder appears in the **Placeholder** tab.
To remove a placeholder, hover its row in the **Placeholder** tab and click the trash icon, then confirm **Delete**. (Only placeholder users can be deleted from the app.)
## Understand the member list
The **Members** page groups people into tabs:
| Tab | Who's here |
| --------------- | ---------------------------------------------- |
| **Active** | Full members with access to your organization. |
| **Placeholder** | Lightweight records without login access. |
| **Removed** | People whose access has been removed. |
Roles (for example **Member**, **Power**, **Admin**, **Owner**) are shown for reference — see [Roles & permissions](/help/admin/roles).
## Teams
Group members into **teams** (for example, by sector or fund) so they share access, prompts, and dashboards.
Go to **Settings → Teams** (under the **Organization** group).
Click **Create Team**. In the **Add a Team** dialog, enter a **Name** and optionally **Industries** and **Sectors**, then click **Create**.
Open a team and choose **Edit Team**, then add people in the **Members** field and click **Update Team**.
Assigning a deal to a team — rather than an individual — keeps coverage intact when people are out or change roles.
## Related
What each role can do.
First-time setup for a new firm.
# Working with companies
Source: https://docs.metal.ai/help/companies
Add companies, organize their files, and read enriched profiles.
Companies are the most central record in Metal. This page covers creating them and reading their profiles.
## Create a company
In the sidebar, expand **Records** and select **Companies**. Click **New Company** in the top right.
In the **Add a Company** dialog, enter the **Company Name** (start typing to match an existing company) and optionally add **Company Tags**. Click **Next**.
You can create a folder for the company's files now, or choose **Skip Folder**.
After the company is created, upload files right away or **Dismiss** to do it later. See [Files & documents](/help/documents).
## Read a company profile
Open a company to see its profile: firmographics, linked people, related deals, and files. Fields filled in by AI show a **View Enrichment** option — see [Enrichment](/help/enrichment).
## Related
Add and link contacts to companies.
Create deals that target your companies.
# Key concepts
Source: https://docs.metal.ai/help/concepts
The core building blocks of Metal, explained in plain language.
A quick, non-technical tour of the ideas you'll see throughout Metal.
## Companies, deals, and people
* A **company** is an organization you track — a target, investor, advisor, or service provider.
* A **deal** is an opportunity, linked to a target company, that moves through your pipeline.
* A **person** is a contact or executive, often linked to a company.
## Files
Upload documents — CIMs, financials, memos — to companies and deals. Metal processes them so their contents turn up when you search. See [Files & documents](/help/documents).
## Lists
**Lists** are grids that group companies, deals, people, or documents so you can work with them together — including AI-enriched and computed columns. See [Working with lists](/help/lists).
## Enrichment and scoring
**Enrichment** fills in details about companies and people automatically. **Scoring** evaluates deals and companies against your firm's frameworks. See [Enrichment](/help/enrichment).
## Workflows
**Workflows** automate multi-step research and produce work product. Each execution is a **run**. See [Using workflows](/help/workflows).
Want the technical data model behind these concepts? See [Data model](/concepts/data-model) in the Developers product.
# Building org-wide dashboards
Source: https://docs.metal.ai/help/dashboards
Create free-standing dashboards in Metal, add widgets through chat, and share them with users, teams, or the whole organization.
**Dashboards** are free-standing views your firm uses to track whatever matters — fund performance, pipeline health, portfolio KPIs, weekly metrics. Unlike the pipeline or per-record dashboards, a top-level dashboard isn't attached to a specific deal, company, list, or user. You build it once, share it with the people who need it, and everyone sees the same data.
Dashboards are rolling out gradually. If you don't see **Dashboards** in your sidebar, reach out to [support@metal.ai](mailto:support@metal.ai) to have it enabled for your firm.
## The Dashboards gallery
Select **Dashboards** in the left sidebar to open the gallery. It's split into two sections:
| Section | What's in it |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| **My Dashboards** | Dashboards you created. |
| **Shared with me** | Dashboards other people have shared directly with you, with your team, or made available to the whole org. |
Each card shows the dashboard's name, description, and whether it's shared. Click a card to open the dashboard.
## Create a dashboard
Select **Dashboards** in the sidebar.
Click **New dashboard** next to the **My Dashboards** heading.
Give the dashboard a **Name** and an optional **Description** (for example, *Q3 Portfolio Review*). Click **Create**.
The new dashboard opens empty, ready for widgets.
## Add widgets
Widgets are the individual charts, tables, and metrics on a dashboard. You add and edit them through the docked chat panel, which opens automatically on the dashboard page.
In the docked chat, describe the widget in plain language — for example, *"Add a widget that shows total AUM by fund over the last 12 months"* or *"Add a table of the top 10 companies by revenue growth"*. The assistant adds the widget to your dashboard.
Click **Edit layout** in the header. Drag widgets to move them, drag their edges to resize, and use the delete control on a widget to remove it. Click **Done** to save.
Every widget is backed by either a **script** (for data queries) or a **prompt** (for AI-generated content). Click the **source** control on a widget to view — or inline-edit — the underlying script or prompt. Widgets you customize are marked with a **Custom** badge.
Because these dashboards aren't scoped to a single deal or company, widgets resolve against your whole organization's data. Any entity filter has to be part of the widget's own query.
## Share a dashboard
You control who can see a dashboard from the **Share** dialog in the dashboard header.
On the dashboard, click **Share** in the header.
Under **Shared with**, add individual **Users** or whole **Teams**. Everyone you add sees the dashboard under **Shared with me** in their gallery.
Toggle **Public** to make the dashboard visible to everyone in your organization. Public dashboards show an **Org-wide** badge in the gallery.
Only the dashboard's creator and its **Owners** can edit the layout, edit widgets, change sharing, or delete the dashboard. Everyone else has read-only access.
## Dashboards vs. Default Dashboards
Metal has two related surfaces. Use this table to pick the right one:
| | **Dashboards** (top-level) | **Default Dashboards** (in Settings) |
| ----------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Where you find it | Sidebar → **Dashboards** | **Settings → Firm Guidance → Default Dashboards** |
| What it is | Free-standing dashboards for the whole org | The default widgets shown on every deal, company, list, or user record |
| Scope of the data | Org-wide — no entity filter | Automatically scoped to the record you're viewing |
| Who edits it | The creator and any owners; anyone can create their own | Admins only |
| Who sees it | You, plus people/teams you share with (or the whole org if public) | Everyone, on every matching record |
If you want a firm-wide reporting view, use a top-level dashboard. If you want every deal (or every company, list, or user profile) to open with the same widgets by default, configure that under **Settings → Default Dashboards**.
## Related
Where admins configure Default Dashboards and other firm-wide preferences.
Set up teams so you can share dashboards with a whole group at once.
# Working with deals
Source: https://docs.metal.ai/help/deals
Create deals, set acquisition type, assign target companies, move them through stages on the Active Pipeline, assign owners, and attach documents in Metal.
A **deal** represents an opportunity your firm is evaluating, linked to a target company. You create and manage deals from the **Deals** area, and track them visually on the **Active Pipeline**.
## Create a deal
In the left sidebar, expand the **Records** group and select **Deals**. Click **New Deal** in the top right. (You can also use the **+** on the **Records** group and choose **Add Deal**.)
A deal must be linked to a company. In the **Create New Deal** dialog, choose the target company under **Company** (required). If it doesn't exist yet, create it first from **Companies** — see [Working with companies](/help/companies).
Give the deal a **Deal Name** (for example, *Acme Corp – Platform*). Optionally set **Status**, **Stage**, **Deal Size**, **Owners**, and other fields. Click **Create Deal**.
New deals default to the **Active** status and the first stage. Your firm's status and stage labels are configured by an admin — see [Taxonomy](/help/admin/taxonomy).
## Set the acquisition type
Every deal has an **Acquisition Type** of either **Platform** or **Add on**. Use it to mark whether the target is a standalone platform investment or an add-on to an existing portfolio company.
| Acquisition Type | What it means |
| ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Platform** | A standalone platform investment. This is the default for new deals. |
| **Add on** | A bolt-on to an existing portfolio company. Selecting **Add on** requires picking the **platform parent company**. |
The field appears directly below the deal name in the **Create New Deal** and **Edit Deal** dialogs, and as **Acquisition Type** on the **Deal Details** widget where you can change it inline. Switching to **Add on** reveals a company picker — choose the portfolio company that the deal is an add-on to. Switching back to **Platform** clears the parent.
The type itself is derived from whether a platform parent is set, so the API returns it as the read-only field `acquisitionType` on the deal.
When a deal is synced from your CRM with writeback enabled, **Acquisition Type** is locked everywhere it appears. The platform parent is managed by your CRM field mapping, and edits in Metal would be ignored. See [CRM integration](/help/integrations/crm).
## Assign or change a deal's company
A deal's target **Company** can be assigned or changed after creation, without leaving the view you're in. Both entry points use the same company picker and save through the deal's usual update flow.
Open the deal and find the **Company** row in the **Deal Details** card. Click the inline company picker to select a different company, or to add one if the deal has no company linked yet.
On the **Deals** page, hover the **Company** cell for a deal and click the pencil to change it. For deals with no company linked, the cell shows an **Add company** button — click it to pick one.
On **add-on** deals, the acquiring (platform) company is hidden from the picker — a deal's target company can't be the same as its platform company.
### Deals sourced from a CRM
For deals that sync from a connected CRM (writeback-enabled orgs), you can **set or change** the company inline from either place; the change is written back to your CRM when the CRM field is mapped. **Clearing** the company on a CRM-sourced deal is not supported and will be rejected.
## Track deals on the Active Pipeline
The **Active Pipeline** (in the sidebar) is where you see deals in motion.
Select **Active Pipeline** in the sidebar. The **Overview** tab shows pipeline dashboards; the **Active Deals** tab lists the deals themselves.
On the **Active Deals** tab, use the view toggle to switch to the **Kanban board view**. Each column is a deal **stage**.
Drag a deal card from one stage column to another to advance it. A confirmation appears once the deal moves. (Drag requires edit permission on the deal.)
Turn on the **My Deals** toggle on the **Active Deals** tab to see only the deals you own.
The **Active Deals** list is backed by a Metal list. Saved views on that list are shared with everyone who has access, while your last selected view in the embedded Active Deals list is remembered for you only. See [Save and use views](/help/lists#save-and-use-views).
Group the Active Pipeline by **Portfolio Company** to see add-on deals clustered under their platform parent. Platform deals and add-ons without a parent set fall into an **Other** bucket. The option appears in the board's group-by selector once the **Acquisition Type** column is present.
### Stage vs. status
Metal tracks two separate things about every deal:
| Concept | What it means |
| ---------- | --------------------------------------------------------------------------- |
| **Stage** | How far the deal is in your process (the kanban columns). |
| **Status** | The deal's overall state — for example **Active**, **Closed**, or **Dead**. |
Both use your firm's own labels, configured by an admin in [Taxonomy](/help/admin/taxonomy).
## View similar deals
Open a deal and look for the **Similar Deals** table on the deal overview. Metal uses your firm's deal history and structured deal data to show prior deals that look similar to the current one.
The **Similarities** column explains why a deal matched. Each row can show chips such as **Subsector**, **Sector**, **Revenue scale**, **EBITDA scale**, **Outcome**, or **Vintage**. If there are several reasons, Metal shows the strongest reason first and collapses the rest behind a **+N** chip. Hover over **+N** to see the remaining reasons.
Similarity chips only appear on the **Similar Deals** table. They do not appear on every deal table or list view. If you do not see the table, the **Similarities** column, or any chips, your firm may not have enough matching deal data yet, or the similar-deals widget may not be configured for that deal view.
## Assign owners
Owners are the people responsible for a deal.
Click the deal from the Deals table or the pipeline board.
Open the deal's actions menu and choose **Edit** to open the **Edit Deal** dialog.
In the **Owners** field, add teammates (you can select individual **Users** or whole **Teams**). Save your changes.
## Attach documents
Documents live on a deal's **Documents** tab and are shared with the deal's linked company.
Open the deal and select the **Documents** tab. The deal must have a linked company; if it doesn't, link one first.
Click **Add File**, or drag files onto the table (you'll see a **Drop files to upload** overlay). Uploaded files are processed and become searchable.
## Related
Manage the target companies your deals link to.
How admins configure deal stages and statuses.
# Files & documents
Source: https://docs.metal.ai/help/documents
Upload files to Metal, find them in search, and see how each one was classified.
Add files — CIMs, financials, memos — so your firm's knowledge lives in one place and becomes searchable. You'll find your documents under **Files** in the sidebar (**My Files**, **Folders**, and any connected storage).
## Upload files
You can upload from a folder or directly onto a company.
Open **Files → My Files** (or any folder), click **Add**, and choose **New File**. You can also drag files onto the area — a **Drop files to upload** overlay appears.
On a company's profile, click **Add File** to upload files scoped to that company.
In the **Add New Files** dialog, add your files and click **Upload**. Files are processed in the background.
Uploaded files are processed automatically so their contents can be found later through search.
## Find documents
Open **Files** and use **Search files and documents**, or press ⌘ K for the global search. See [Searching in Metal](/help/search). You can filter the Files view by company, document type, and source.
## Document types
Every file Metal processes is tagged with a **document type** — for example *CIM*, *Financial Statement*, *Board Deck*, or *Quality of Earnings*. The document type appears as a small badge wherever the file shows up: the Files table, integration browsers (Egnyte, SharePoint, Box, ShareFile, Google Drive), the file detail view, activities, and global search results.
### Reading the badge
The icon on the badge tells you who set the type:
* **Sparkle icon — Classified by Metal.** Metal's classifier (or its verifier) chose this type from the document's contents.
* **Person icon — Set by a user.** Someone at your firm picked or changed the type by hand.
* **No icon.** A legacy classification with no recorded provenance.
Hover the badge to see a tooltip with more detail. For Metal-classified documents the tooltip shows:
* A **confidence band** — *High*, *Medium*, or *Low* — summarizing how sure the classifier was.
* A short **rationale** explaining why Metal chose that type based on what it read in the document.
User-set types show only "Set by a user" — no confidence or rationale, because a person made the call.
### Correcting a document type
If Metal got the type wrong, change it on the file's detail view. Your selection is recorded as a user label, which clears Metal's prior rationale and confidence so the badge no longer suggests the AI's reasoning still applies. The new type also feeds back into extraction — see [Settings → Extraction](/concepts/enrichment) for how document types control which metrics Metal pulls from each file.
## Connected storage
If your firm has connected Egnyte, SharePoint, Box, ShareFile, or Google Drive, you can browse those sources under **Files** too. See [File storage integrations](/help/integrations/file-storage).
# Enrichment
Source: https://docs.metal.ai/help/enrichment
Let Metal fill in firmographics, financials, and signals with AI.
Enrichment fills in what you don't already know about companies and people — automatically, using AI and data providers. In Metal, enrichment happens in three places: automatically for new records, across a list, and on individual fields.
## Automatic enrichment (admin)
Admins choose which properties Metal enriches automatically for new companies and people.
Go to **Settings → Enrichment**.
On the **Company** and **Person** tabs, toggle the properties you want enriched. Use **Re-run** to refresh a property, and **Discover people** to enrich people associated with each company.
Click **Save changes**.
## Enrich a list in bulk
To enrich many records at once, use a [list](/help/lists):
On a list, add a column and choose **Enable AI Enrichment**.
From the column header menu, choose **Refresh Column** to enrich every row.
## Enriched fields on a record
On a company or person, fields filled in by AI show a **View Enrichment** option so you can see where a value came from. You can pin or override a value from the field editor.
The more identifying detail a record has (like a website), the more accurate enrichment is.
# Metal user guide: get oriented and find your way
Source: https://docs.metal.ai/help/index
Start here to learn the Metal app — navigation, key concepts, working with deals, admin setup, integrations, and how to reach support.
Metal helps your firm turn institutional knowledge into conviction across the deal lifecycle. This guide walks through the app feature by feature — how to navigate it, work day to day, and administer it for your team.
Looking for the API, MCP, or integration code? Switch to **Developers** in the product menu at the top of the page.
## Start here
Learn the layout — where companies, deals, people, and documents live.
Understand the building blocks in plain language.
Track opportunities through your pipeline.
Invite teammates and control their access.
## Work feature by feature
## Administer your workspace
Set up Metal for your firm for the first time.
Decide who can see and do what.
Configure workspace-wide preferences.
Connect your CRM, file storage, and more.
## Need a hand?
Open **Support** from your account menu in the bottom-left of the app to reach the in-app support portal. From there you can:
* Open the **Knowledge Base** at [docs.metal.ai](https://docs.metal.ai) for guides and documentation.
* Use **Contact Us** to email the Metal support team — include a screenshot and the page you were on for the fastest help.
* Open the **Customer Tracker** to view your organization's support tickets with status and priority filters. This tile only appears to organization admins when the support tracker is enabled. Ask your Metal contact if you expect to see it but don't.
# CRM integration
Source: https://docs.metal.ai/help/integrations/crm
Connect Metal to your CRM to keep deals and companies in sync.
Connect your CRM so companies and deals stay in sync between systems. Metal supports **DealCloud**, **Salesforce**, **Affinity**, and **Dynamo**.
## Enable a CRM (admin)
Go to **Settings → Integrations** and find your CRM.
Click into the CRM and follow its setup. Some CRMs need credentials — for example, **DealCloud** asks for a **Client ID**, **API Key**, and **Base Url** before you click **Connect**.
## Connect your account (user)
Where a CRM supports per-user sign-in, connect your account at **Settings → Connections** once an admin has enabled it.
Setting up a CRM is part of onboarding — the **Setup** checklist includes an **Integrate CRM** step. See [Onboarding your workspace](/help/admin/onboarding).
# Microsoft Exchange integration for mail and calendar
Source: https://docs.metal.ai/help/integrations/exchange
Connect your Microsoft Exchange mailbox to ingest mail and calendar into Metal and use the same live mailbox tools as Outlook in chat.
The Microsoft Exchange integration ingests each connected user's mail and calendar into Metal so they show up alongside the rest of your firm's context. It also enables the same live mailbox tools as the **Outlook** connection in chat.
## Connect your mailbox
Like the Outlook connection, Exchange is a per-user connection — you connect your own mailbox from **Settings → Connections**, with no separate admin step.
Go to **Settings → Connections → Microsoft Exchange**.
Click **Add Microsoft Exchange Connection**, then **Connect**, and complete the Microsoft sign-in. When you're back in Metal, the page shows your name and email with a **Connected** badge.
Your initial mail and calendar import may take some time. After that, Metal checks for new and changed items approximately every five minutes.
To remove your mailbox, click **Disconnect** on the same page.
Exchange and Outlook are different connection options. Both enable live mailbox tools in chat. Exchange also persists mail and calendar for each connected user so that information becomes part of your firm's context.
# File storage integration
Source: https://docs.metal.ai/help/integrations/file-storage
Connect file storage so documents flow into Metal and become searchable.
Connect file storage so your documents are available in Metal and become searchable. Supported providers are **SharePoint**, **Egnyte**, **Box**, **ShareFile**, and **Google Drive**.
## Enable a provider (admin)
Go to **Settings → Integrations** and choose your provider.
Follow the provider's setup. For example, **Egnyte** asks you to **Enable Egnyte** and enter your **Domain Name**.
## Connect your account (user)
Go to **Settings → Connections** and find the provider your firm enabled.
Click **Connect** and authorize access. You'll see a **Connected** badge when it's set up.
## Browse connected files
Once connected, browse the provider under **Files** in the sidebar. Documents you bring in are processed so their contents turn up in [search](/help/search).
# External MCP servers
Source: https://docs.metal.ai/help/integrations/mcp
Connect third-party MCP servers to Metal using OAuth discovery, Dynamic Client Registration, static OAuth clients, bearer API keys, or no authentication.
Metal can connect to remote [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers so people in your organization can use their tools in Metal.
This page covers connecting an external MCP server **to Metal**. To connect an
AI tool such as Claude, ChatGPT, or Cursor to Metal's hosted MCP server, see
the [Metal MCP server guide](/mcp/overview).
## How setup works
MCP connections have two layers:
1. An admin adds the MCP server under **Settings → Integrations** and chooses how it authenticates.
2. Each user connects their own account under **Settings → Connections**.
The server configuration is shared across your organization. OAuth tokens and API keys belong to the individual user, are stored encrypted, and are used only for that user's connection.
## Choose an authentication method
| Method | Use it when | What each user does |
| ----------- | -------------------------------------------------------------------------------------- | --------------------------------------------- |
| **OAuth** | The server has OAuth authorization and token endpoints. | Signs in to the provider and approves access. |
| **API Key** | The server accepts a personal API key as a bearer token in the `Authorization` header. | Pastes their own API key in Metal. |
| **None** | The server is intentionally available without credentials. | Clicks **Connect**. |
Metal sends API keys in the `Authorization: Bearer ` header. The request
header is not configurable. Providers that require a custom header such as
`x-api-key` need to support OAuth or bearer-token authentication before they
can be connected this way.
## Add a server
As an admin, go to **Settings → Integrations** and click **Add MCP Connection**.
Enter a name, the remote MCP server URL, and an optional description. Use the complete remote MCP endpoint supplied by the provider.
Select **OAuth**, **API Key**, or **None**. For OAuth, continue with the setup below.
Click **Create Server**. The server becomes available to members of your organization under **Settings → Connections**.
## Configure OAuth
Metal uses the OAuth authorization code flow with PKCE. Each user authorizes their own provider account.
After entering the server URL, Metal attempts server discovery automatically. You can also click **Discover Server**. Discovery looks for the provider's authorization endpoint, token endpoint, and Dynamic Client Registration endpoint. It may also show tools, resources, and prompts when the server exposes them without authentication.
Discovery can succeed even when capabilities remain empty because many servers require authentication before returning their capabilities.
### Dynamic Client Registration
Use Dynamic Client Registration (DCR) when discovery reports **Dynamic Registration** as supported. Metal registers an OAuth client with the provider for your organization, so you do not need to enter a client ID or secret.
The provider's authorization server must accept Metal's registration request and callback URL. If it restricts permitted redirect URIs or OAuth clients, its administrator must allow Metal before users can connect.
### Statically configured OAuth client
Use a static OAuth client when the provider does not support DCR or requires clients to be registered in advance.
Create an OAuth application in the provider's administration console. Register this exact callback URL:
```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
https://api.metal.ai/v1/integrations/oauth/mcp/callback
```
In Metal, enter the provider's **Authorization URL** and **Token URL**. **Discover Server** may fill these fields automatically.
Enter the registered **Client ID**. Add the **Client Secret** when the provider created a confidential client. Leave it blank for a public client that uses PKCE without a secret.
Leave **Dynamic Client Registration** disabled, then click **Create Server**.
The client ID and optional secret are shared OAuth configuration for your organization. The secret is stored encrypted and is not displayed again after you save the server.
Custom MCP server setup does not currently include a field for OAuth scopes. The provider must supply suitable default scopes. Contact Metal support if the provider requires Metal to request an explicit scope list.
If a provider asks whether Metal must be added to an allowlist of permitted
MCP or OAuth clients, the answer is usually yes when that provider does not
support DCR. Register the static client and allow the callback URL above.
## Connect your account
After an admin adds the server:
1. Go to **Settings → Connections**.
2. Find the MCP server and click **Connect**.
3. Complete the provider sign-in, paste your API key, or confirm the unauthenticated connection, depending on the method the admin selected.
OAuth access and refresh tokens are stored per user. API keys are also stored per user. Disconnecting your connection does not remove the server for the rest of the organization.
## Permissions and ownership
Only admins can add, configure, or remove an external MCP server for the organization. After the server is added, organization members manage their own connection under **Settings → Connections**.
The server URL, authentication method, and static OAuth client configuration are organization-wide. OAuth tokens and API keys are per-user credentials. A user can disconnect or retry their own connection without changing the shared server configuration or another user's connection.
## Troubleshooting
Confirm that the MCP server URL is correct and reachable by Metal. You can
still enter the authorization URL, token URL, and static client credentials
manually when the provider does not publish discovery metadata.
The server does not advertise DCR and no static client ID is configured.
Ask the provider administrator to register Metal, then add the client ID and
optional client secret to the server configuration.
Add `https://api.metal.ai/v1/integrations/oauth/mcp/callback` to the OAuth
application's allowed redirect URIs. The value must match exactly.
The provider may have disabled registration, restricted client metadata, or
require an allowlisted client. Use a statically configured OAuth client when
the provider does not support open DCR.
Confirm that the key belongs to the connecting user and is still active.
The provider must accept it as `Authorization: Bearer `. Custom API key
headers are not currently supported.
Go to **Settings → Connections** and click **Retry** on the failed
connection. OAuth connections may require a new provider sign-in when a
token expires or cannot be refreshed. API key connections prompt for a
replacement key.
# Integrations overview
Source: https://docs.metal.ai/help/integrations/overview
Connect Metal to your firm's file storage, CRM, Microsoft Exchange mailboxes, and market data sources through admin and per-user connections.
Integrations keep Metal in sync with the systems your firm already uses. Most work in two layers: an admin **enables** an integration for the whole firm, and each user **connects** their own account. Some connections, including Microsoft Exchange, are per-user only and do not require an admin enablement step.
## Admin: enable integrations
Admins manage integrations that require firm-wide setup at **Settings → Integrations**. Browse available integrations under **Browse Connections**, and manage the ones you've turned on under **Installed**. Each card offers **Install** (to enable) or **Configure**.
## Users: connect your account
Connect supported personal accounts at **Settings → Connections**. For integrations that require firm-wide enablement, an admin must enable them first. Per-user-only connections such as Microsoft Exchange can be connected directly. Each row offers **Connect** or **Configure**, and shows a **Connected** badge when you're set up.
## What you can connect
SharePoint, Egnyte, Box, ShareFile, and Google Drive.
DealCloud, Salesforce, Affinity, and Dynamo.
Bring mail and calendar into Metal for each connected user, and use live mailbox tools in chat.
PitchBook and other enabled data sources for firm research and workflows.
Add remote MCP servers with OAuth, API key, or no authentication.
Data rooms are usually folders and files inside Metal, not a separate vendor
connector. You can bring data-room material in through connected file storage
or upload it directly, then search and analyze it in Metal.
# Investor relations: funds, LPs, fundraising
Source: https://docs.metal.ai/help/investor-relations
Track funds, limited partners, and fundraising processes in Metal with dedicated detail pages, lists, and configurable fundraising taxonomy.
Metal's **Investor Relations** area gives your firm a place to track the capital side of the business alongside your deals — the **funds** you manage, the **limited partners** who invest in them, and the **fundraising processes** you're running to bring new capital in.
Investor Relations is rolling out gradually. If you don't see **Funds**, **Limited Partners**, or **Fundraising Processes** in your sidebar yet, it hasn't been turned on for your organization — reach out to [support@metal.ai](mailto:support@metal.ai) to enable it.
## Investor Relations records
Investor Relations adds three record types in its own sidebar section:
* **Funds** — the investment vehicles your firm manages. Each fund has a detail page with status, currency, target and committed capital, close dates, strategy, and description.
* **Limited Partners** — investors and prospective investors in your funds. LPs can be linked to a company record, hold primary and consultant contacts, and carry strategy and geography preferences.
* **Fundraising Processes** — active capital raises. Each process links a fund to the limited partner you're raising from, has its own status and stage, and tracks the probability of closing along with expected and actual close dates.
Open any table page to see every record of that type. Click a row to open the record's detail page.
## Fund, LP, and fundraising process detail pages
Each record has a full detail page (for example, `/funds/{id}`) with a dashboard-style layout and inline-editable fields. From the detail page you can:
* Click any field on the detail card — status, stage, currency, close dates, description, linked fund or LP — to edit it in place. Foreign-key fields (fund on a process, limited partner on a process) open a search picker.
* Use **Edit** in the header to open a full edit dialog, or **Delete** to remove the record.
* See any custom fields your admins have added to that record type.
If fields on a detail card are read-only, inline editing may not be enabled for your organization. Contact [support@metal.ai](mailto:support@metal.ai) for help.
## The IR table pages
The **Funds**, **Limited Partners**, and **Fundraising Processes** table pages share the same conveniences:
* **Search** — the search box at the top of the page filters rows by text as you type.
* **Column toggles** — use the columns menu to show or hide fields. Columns keep their original order when you toggle them back on.
* **Loading overlay** — the table shows a spinner overlay while its first set of records loads.
## Lists of IR records
You can also build [lists](/help/lists) of limited partners or fundraising processes — the same way you'd build a list of companies or deals.
On the **Lists** page, click **New List**. In the **Create New List** dialog, pick **Limited Partners** or **Fundraising Processes** as the **Type**.
Open the new list and click **Add** to search and select records.
Use **Add Column** to bring in fields from the underlying record. An LP's **Primary Contacts** and **Consultant Contacts** render as named chips. A process's **Owners** and **Assignees** render as user avatars.
Fund lists aren't offered as a standalone list type today — funds appear as linked references from other IR records instead.
For everything else about lists — views, filters, grouping, saved column layouts, AI enrichment, and computed columns — see [Working with lists](/help/lists).
## Configure fundraising taxonomy
Fundraising processes have their own **status** and **stage** values, separate from deal status and stage. Admins configure them in **Settings → Taxonomy** — see [Taxonomy](/help/admin/taxonomy).
## Related
Build lists of limited partners or fundraising processes with reference-list columns.
Configure fundraising status and stage labels for your firm.
# Working with lists
Source: https://docs.metal.ai/help/lists
Group companies, people, deals, documents, CIMs, limited partners, or fundraising processes into lists and enrich or compute columns.
Lists are grids that gather records so you can work with them together — building a screening universe, tracking a theme, or enriching many companies at once. Open **Lists** in the sidebar; lists are organized into **My Lists**, **Shared with Me**, and **Firmwide**.
## Create a list
On the **Lists** page, click **New List**.
In the **Create New List** dialog, enter a **Name** and choose a **Type** — Companies, People, Deals, Documents, CIMs, and (when Investor Relations is enabled) Limited Partners or Fundraising Processes. Optionally add a description.
Click **Continue**. Your list opens, ready for records.
## Add records
Open a list and use the add control for its type:
* **Companies or people** — click **Add** and choose **Search & select**, **Paste a list**, or **Upload a file**.
* **Deals** — click **Add** and choose **Search & select** or **Upload a file**.
* **Limited partners or fundraising processes** — click **Add** to search and select records. Use **Import** in the toolbar to upload a file.
* **Documents or CIMs** — use **Add entry** / **Add Row**.
Lists of limited partners or fundraising processes can also include reference fields. An LP's primary or consultant contacts render as named chips, while a fundraising process's owners and assignees render as user avatars. See [Investor relations](/help/investor-relations) for what these records cover.
## Enriched and computed columns
Lists can compute new columns with AI.
Use **Add Column** from the column menu.
Enable **AI Enrichment** to pull in data, or **Enable Computed Column** to derive values from other columns.
From a column's header menu, choose **Refresh Column** (enrichment) or **Recompute Column** to update every row at once.
Lists are the efficient way to [enrich](/help/enrichment) many records at once instead of one at a time.
## Sort, filter, and group
Every list has header controls for narrowing and organizing rows:
* **Filter** — click a column header and add a filter to keep only rows that match. Add filters on as many columns as you like.
* **Sort** — click a column header to sort by it. Hold **Shift** and click another column header to add a second sort; the numbered indicator shows the sort order.
* **Group by** — from a groupable column's header menu, choose **Group by** to collapse rows into groups. Grouping is exclusive with a saved View's grouping and, on the Active Pipeline, with the **My Deals** toggle.
You can also reorder and pin columns from their header menu. To keep the new column order and pins as the list's defaults for everyone, use **Save column layout** from the list's overflow menu.
## Save and use views
A **View** is a named combination of filters, sorts, and an optional grouping saved on a list. Use views to switch between different ways of looking at the same records — for example, "Active by owner" versus "All, sorted by last activity" — without rebuilding the filters each time.
Views work on any list you can open, including system lists.
Saved views belong to the list, not to one user. Anyone who can access the list can see its saved views. The list's default view also applies to everyone with access.
Apply the filters, sorts (including multi-column sort), and grouping you want to save.
Click **Views** in the list's toolbar, then choose **Create view**.
Give the view a name. If you have permission to change list defaults, you can also turn on **Set as default** so this view is what everyone sees when they open the list. Click **Save**.
Once saved, the view appears in the **Views** menu. The trigger button shows the active view's name.
In embedded list widgets, such as the **Active Deals** list on the **Active Pipeline**, Metal remembers your last selected view for that list in your browser. That personal selection does not change the shared default view.
### Apply, edit, or delete a view
Open the **Views** menu on the list page or the list widget to:
* **Apply** a view — click its name. Click it again to clear back to the base list.
* **Edit** a view — open the view's actions and choose **Edit** to change its filters, sorts, grouping, or name.
* **Delete** a view — from the editor, choose **Delete**.
* **Set as default** — inside the editor, toggle **Set as default**. This changes what other members see and is restricted to list owners and admins.
A list can have one default view, or no default at all. When a column referenced by a saved view is deleted or changes type, the invalidated filter, sort, or grouping is pruned from the view automatically.
### Views, grouping, and My Deals
Only one organizing mode can be applied at a time. Metal shows a toast when it clears one to activate another:
* Applying a **View** clears an ad-hoc grouping and turns off **My Deals**.
* Turning on **My Deals** (Active Pipeline only) clears the active view and any grouping.
* Setting a **Group by** clears the active view.
## Deal-specific columns
Deal lists include an **Acquisition Type** column you can edit inline. The cell shows **Platform** or **Add on**; choosing **Add on** prompts you to pick the platform parent company. The cell is read-only when the deal is synced from your CRM with writeback enabled — see [Working with deals](/help/deals#set-the-acquisition-type).
# Navigating Metal
Source: https://docs.metal.ai/help/navigating
Find your way around the Metal app — the sidebar, search, and your account menu.
This page is your map of the Metal app: where everything lives and how the pieces connect.
## The sidebar
The left sidebar is your main way to move around. From top to bottom:
* **Search** — opens a command palette to jump to anything (see below).
* **Home** — your starting point, with chat and an overview.
* **Active Pipeline** — track deals in motion on a board or list.
* **Projects** — your project workspaces.
* **Workflows** — run and review AI [workflows](/help/workflows).
* **Lists** — build and manage [lists](/help/lists).
* **Dashboards** — build and share org-wide [dashboards](/help/dashboards) (when enabled for your firm).
* **Files** — your documents, with sub-items like **My Files**, **CIM Library**, and **Folders**, plus any connected storage (Egnyte, SharePoint, Box, ShareFile, Google Drive).
Under the **Records** group you'll find your data:
* **Companies**, **Deals**, **People**, and (when enabled) **Portfolio** and **Activities**.
* Use the **+** on the Records group to quickly add a record.
## Search and chat
* **Search** (press ⌘ K) opens a command palette to find companies, deals, files, and pages.
* **Chat** (press ⌘ J) opens the AI assistant on supported pages.
See [Searching in Metal](/help/search) for details.
## Your account menu
Open the menu in the bottom-left (your name/avatar) to reach:
* **Settings** — personal and (for admins) firm settings.
* **Theme** — Light, Dark, or System.
* **Feedback** and **Support**.
* **Log out**.
Belong to more than one organization? Use **Switch organization** in the sidebar header to move between them.
# Working with people
Source: https://docs.metal.ai/help/people
Add contacts and executives and link them to companies.
People are the contacts you track — executives, management teams, and advisors. This page covers adding people and linking them to companies.
## Add a person
In the sidebar, expand **Records** and select **People**. Click **Add Person**.
In the **Create New Person** dialog, fill in **First Name**, **Last Name**, and **Email** (required). Optionally add **Title**, **Company**, **LinkedIn**, and more.
Click **Create Person**.
## Link a person to a company
You can also add a person directly from a company, which links them automatically.
Go to the company's profile.
Click **Add Person** and enter their details (**First Name**, **Last Name**, **Title**, **LinkedIn URL**). They're linked to that company on save.
# Searching in Metal
Source: https://docs.metal.ai/help/search
Use the command palette and Files search to find what you need.
Metal has two ways to find things fast: a global command palette and a dedicated Files search.
## Global search (command palette)
Press ⌘ K (or click **Search** in the sidebar) to open the command palette. Start typing to jump to:
* **Pages** — Home, Companies, Folders, and more.
* **Companies** and **Deals**.
* **Files** — matched by name.
The command palette matches records and files by name. To search across the *contents* of your documents, use Files search.
## Files search
Open **Files** and use **Search files and documents**, then press Enter. This searches across your ingested documents, not just titles. Narrow results with the company, document type, and source filters.
## Search vs. chat
Search finds existing records and files. The AI **Chat** (press ⌘ J) is conversational — ask questions and have Metal reason over your firm's context. They're separate tools for different jobs.
# Run and review Metal AI workflows
Source: https://docs.metal.ai/help/workflows
Run a Metal AI workflow, provide required and optional inputs, follow it on the run canvas, and review the output, artifacts, and reports.
Workflows automate multi-step research — screening targets, gathering data, drafting work product — so your team doesn't do it by hand. Open **Workflows** in the sidebar to see the ones available to you.
## Run a workflow
On the **Workflows** page, find the one you want and click **Run**. (Workflows that run on events or email show **View runs** instead.)
In the **Run workflow** dialog, fill in any required inputs, then click **Run**. Optional fields can be left blank — a blank optional field is omitted from the run rather than sent as an empty value. If **Run** is disabled, its tooltip names the specific required fields that still need a value. Fields supplied by a webhook or event trigger, and fields reserved for manual runs, appear in their own section. They are optional for manual runs; on triggered runs, the workflow fills them itself.
You're taken to the run page as it executes.
You can also start workflows from resource pages when the workflow is meant to run against a specific company, deal, or project. Look for **Run Workflow** on the company, deal, or project page, then choose the workflow and provide its inputs.
## Run a comparable-company workflow
Comparable-company workflows, sometimes called comparable-deals workflows, are configured for specific organizations. They do not appear as a separate default section for every customer.
If your organization has one configured:
1. Open **Workflows** from the sidebar, or open the relevant company, deal, or project page and click **Run Workflow**.
2. Select the comparable-company workflow.
3. Provide the required inputs. These usually include the target company or deal, and may include documents, folders, or scoring context depending on how your workflow is configured.
4. Open the run page to watch progress and review the output.
If you do not see the workflow, it usually means one of these is true:
* The workflow has not been configured for your organization yet.
* You do not have permission to see or run it.
* The workflow is private, unpublished, or tied to an event/email trigger rather than manual runs.
Ask your Metal admin or Metal contact to confirm the workflow is configured, published, and shared with the right users.
## Review results
On a run page you'll find:
* A **Steps** tab showing each step as it runs.
* An **Output** tab with the result once the run completes (it opens automatically).
* Downloadable artifacts, including an **Output report** where applicable.
Find past runs under **Recent Runs** on the Workflows page.
### Run canvas
Workflows that use conditional **branch** steps are drawn on a canvas that lays each arm out as its own lane. The lane the run took is drawn normally; the arm that was not taken is dimmed, along with the edges into it, so you can see at a glance which path the workflow chose. Steps in the dimmed arm are marked as skipped, and you can still open them to see what they would have run. Click any card — including a skipped one — to open its details in a pane beside the canvas without collapsing the rest of the run.
## Run workflows from MCP
If your admin has enabled the [Metal MCP server](/mcp/overview), an MCP-compatible assistant can run the same workflows you can access in the app.
Ask the assistant to:
1. Use `list_workflows` to find the comparable-company workflow.
2. Use `get_workflow` to inspect the required input schema.
3. Use `run_workflow` with the required inputs.
4. Use `get_workflow_run` to poll the run and read the output, artifacts, and citations.
MCP access follows your Metal user permissions. If the assistant cannot find the workflow, confirm that you can see and run it in the Metal app first.
Building automations programmatically? See [Automate research with workflows](/guides/automate-workflows) in the Developers product.
# Metal
Source: https://docs.metal.ai/index
Build on Metal, the AI context layer for financial firms.
Metal is the AI context layer for financial firms — the source of truth that takes the messy, fragmented data scattered across your systems and consolidates it into a single, accurate, and complete view. It sits between your systems of record and the AI tools you already use, turning everything your firm knows — deals, documents, and the decisions behind them — into structured, AI-ready context.
The Metal API and [MCP server](/mcp/overview) give your applications and AI agents programmatic access to that context: companies, deals, people, documents, lists, and workflows. Push your firm's data into Metal, enrich and structure it, search across everything you know, and run AI workflows — so your AI reasons with your firm's judgment, not just its files.
Using the Metal app rather than building on the API? Switch to the **User guide** in the product menu at the top of the page.
Make your first authenticated request in a few minutes.
Create API keys and authenticate your requests.
Task-oriented walkthroughs for syncing, enriching, and automating.
Explore every endpoint with an interactive playground.
Use Metal in Cursor, Claude, and ChatGPT over MCP.
Understand companies, deals, people, and how they relate.
## What you can build
Create and update companies, deals, and people from your CRM or internal systems.
Trigger enrichment to fill in firmographics, financials, and signals.
Run AI workflows to screen targets and generate work product.
## Base URL
All API requests are made to the Metal API gateway:
```
https://api.metal.ai
```
Every endpoint is versioned under the `/v1` prefix and returns JSON. Successful responses wrap their payload in a top-level `data` field.
## Get help
Need a hand? Reach out at [support@metal.ai](mailto:support@metal.ai) and include the request method, path, and any error response you received.
# Connect ChatGPT
Source: https://docs.metal.ai/mcp/chatgpt
Add the Metal MCP server to ChatGPT as a connector.
Connect ChatGPT to the Metal MCP server so it can search and reason over your firm's Metal data.
## Prerequisites
* An organization admin has enabled MCP (see [overview](/mcp/overview#prerequisites)).
* A ChatGPT plan that supports custom connectors / developer mode. Availability and admin controls vary by plan (Plus, Pro, Business, Enterprise).
## Add the connector
In ChatGPT, go to **Settings → Connectors** (under **Apps & Connectors**). If you're adding your own server, enable **Developer mode** under **Advanced**.
Click **Create** (or **Add custom connector**) and enter:
* **Name**: Metal
* **MCP server URL**: `https://mcp.metal.ai/mcp`
* **Authentication**: OAuth
Create the connector, then **Connect**. ChatGPT opens a browser window to sign in with your Metal account. Approve access, and choose an organization if prompted.
Start a new chat, open the tools menu, and enable the **Metal** connector for the conversation.
## Verify
Ask ChatGPT something that uses your data, for example:
> Use the Metal connector to find companies in my pipeline tagged as targets and summarize them.
## Troubleshooting
* **Can't add a connector**: custom connectors and developer mode depend on your plan and may be restricted by a workspace admin.
* **Connection or sign-in fails**: confirm an admin enabled MCP under **Settings → Organization → API & MCP Access**, and that the URL is exactly `https://mcp.metal.ai/mcp`.
See the [tools reference](/mcp/tools-reference) for parameters and response examples, and the [overview](/mcp/overview) for shared troubleshooting.
# Connect Claude
Source: https://docs.metal.ai/mcp/claude
Add the Metal MCP server to Claude as a custom connector.
Connect Claude to the Metal MCP server so Claude can work with your firm's Metal data.
## Prerequisites
* An organization admin has enabled MCP (see [overview](/mcp/overview#prerequisites)).
* A Claude plan that supports custom connectors. On Team and Enterprise plans, an admin may need to allow custom connectors first.
## Claude web and desktop
Add Metal as a custom connector in Claude.
In Claude, go to **Settings → Connectors**, then click **Add custom connector**.
Give it a name like **Metal** and paste the server URL:
```
https://mcp.metal.ai/mcp
```
Click **Add**, then **Connect**. Claude opens a browser window to sign in with your Metal account. Approve access, and choose an organization if prompted.
Open the tools/connectors menu in a conversation and make sure **Metal** is enabled.
## Claude Code (CLI)
Add the server with the Claude Code CLI:
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
claude mcp add --transport http metal https://mcp.metal.ai/mcp
```
Then start Claude Code and run `/mcp` to complete the browser sign-in.
## Verify
Ask Claude something that uses your data, for example:
> Use Metal to find recent deals in the industrials sector and list their stages.
Claude requests permission to use the Metal tools on first use. Approve it.
## Troubleshooting
* **"Couldn't connect"**: confirm an admin enabled MCP under **Settings → Organization → API & MCP Access**, and that the URL is exactly `https://mcp.metal.ai/mcp`.
* **Connector option missing**: custom connectors require a supported plan; on Team/Enterprise an admin may need to enable them.
See the [tools reference](/mcp/tools-reference) for parameters and response examples, and the [overview](/mcp/overview) for shared troubleshooting.
# Other MCP clients
Source: https://docs.metal.ai/mcp/clients
Connect any MCP-compatible tool to the Metal MCP server.
Any tool that supports remote MCP servers can connect to Metal at:
```
https://mcp.metal.ai/mcp
```
The server uses streamable HTTP transport and OAuth authentication. Make sure an organization admin has [enabled MCP](/mcp/overview#prerequisites) first.
## Clients that support remote MCP
Add the URL using the client's standard MCP configuration. Most clients accept an entry like:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"metal": {
"url": "https://mcp.metal.ai/mcp"
}
}
}
```
On first use, the client opens a browser window to sign in with your Metal account.
## VS Code (GitHub Copilot)
Add a `.vscode/mcp.json` file to your workspace:
```json .vscode/mcp.json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"servers": {
"metal": {
"type": "http",
"url": "https://mcp.metal.ai/mcp"
}
}
}
```
Open the MCP view, start the **metal** server, and complete the browser sign-in.
## Clients that only support local (stdio) servers
Some clients can only launch a local command. Bridge to the hosted server with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"metal": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.metal.ai/mcp"]
}
}
}
```
`mcp-remote` handles the OAuth flow and forwards requests to the Metal MCP server.
## Troubleshooting
* **Authentication loops or fails**: confirm an admin enabled MCP under **Settings → Organization → API & MCP Access**.
* **Tools don't appear**: verify the URL includes the `/mcp` path and that your client supports remote (HTTP) MCP servers, or use the `mcp-remote` bridge above.
See the [tools reference](/mcp/tools-reference) for parameters and response examples, and the [overview](/mcp/overview) for shared troubleshooting.
# Connect Cursor
Source: https://docs.metal.ai/mcp/cursor
Add the Metal MCP server to Cursor.
Connect Cursor to the Metal MCP server to use your firm's data directly in the Cursor agent.
## Prerequisites
* An organization admin has enabled MCP (see [overview](/mcp/overview#prerequisites)).
* A recent version of Cursor that supports remote MCP servers.
## Add the server
Go to **Cursor Settings → MCP & Integrations**, then click **New MCP server** (or **Add custom MCP**).
Add an entry pointing at the Metal server URL:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"metal": {
"url": "https://mcp.metal.ai/mcp"
}
}
}
```
Cursor opens a browser window to sign in with your Metal account. Approve access, and choose an organization if prompted.
Add Metal to your Cursor MCP config. Use `~/.cursor/mcp.json` for all projects, or `.cursor/mcp.json` in a project root.
```json .cursor/mcp.json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"mcpServers": {
"metal": {
"url": "https://mcp.metal.ai/mcp"
}
}
}
```
Reopen Cursor, then complete the browser sign-in when prompted.
## Verify
Open the Cursor agent and confirm a **metal** server appears in the MCP tools list with a green/connected status. Ask the agent something that uses your data, for example:
> Search Metal for industrial automation companies and summarize the top three.
## Troubleshooting
* **Server shows as disconnected**: make sure an admin enabled MCP under **Settings → Organization → API & MCP Access**, then toggle the server off and on in Cursor to re-run sign-in.
* **No tools listed**: confirm the URL is exactly `https://mcp.metal.ai/mcp` (including the `/mcp` path).
See the [tools reference](/mcp/tools-reference) for parameters and response examples, and the [overview](/mcp/overview) for shared troubleshooting.
# Metal MCP server
Source: https://docs.metal.ai/mcp/overview
Connect Cursor, Claude, ChatGPT, and other MCP clients to Metal to search companies, deals, people, and documents, and run firm workflows.
The Metal MCP server lets MCP-compatible AI tools like Cursor, Claude, and ChatGPT securely access your firm's Metal data. Once connected, your assistant can search companies, deals, people, and documents, and run workflows, grounded in your organization's knowledge.
[Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open standard for connecting AI applications to external tools and data.
## Server URL
Connect your AI tool to the hosted Metal MCP server:
```
https://mcp.metal.ai/mcp
```
The server speaks streamable HTTP and authenticates with OAuth, so there's no API key to manage. When you connect, your tool opens a browser window to sign in with your Metal account. If you belong to more than one organization, you choose which one to connect.
Access is scoped to your Metal user and organization, and respects the same permissions you have in the app.
## Prerequisites
An organization admin must turn on the MCP server first: in the Metal app, go to **Settings → Organization → API & MCP Access** and enable **Allow access to the Metal MCP server**.
You need a client that supports remote MCP servers, such as [Cursor](/mcp/cursor), [Claude](/mcp/claude), or [ChatGPT](/mcp/chatgpt). See [other clients](/mcp/clients) for everything else.
## Connect your tool
Add Metal as a remote MCP server in Cursor.
Add Metal as a connector in Claude.
Add Metal as a connector in ChatGPT.
Using a different client? See [other MCP clients](/mcp/clients).
## Portability and interoperability
Metal exposes your firm's context through standards-based interfaces, so your data is not locked into one chat surface or one model provider.
* **Open agent protocol**: MCP is an open standard. Any MCP-compatible tool can connect to `https://mcp.metal.ai/mcp`, including tools such as Cursor, Claude, ChatGPT, and clients that bridge local-only MCP support with `mcp-remote`.
* **Programmatic API access**: the Metal REST API returns JSON for the same core records your team works with in the app, including companies, deals, people, documents, activities, lists, enrichments, scores, [financial metrics](/concepts/financial-metrics), workflows, and [data observations](/api-reference/data/get-an-observation). Use the API to sync records in, read enriched context out, and feed downstream systems. See the [API reference](/api-reference/introduction).
* **Customer-controlled identifiers**: companies, deals, and people can carry your own `externalId` and `externalReference` values, which makes it easier to reconcile Metal records with your CRM, data warehouse, or other systems. See the [data model](/concepts/data-model#identifiers).
* **Permission-aware access**: MCP uses OAuth and API requests use organization-scoped API keys, so connected tools only see data they are allowed to access.
## Available tools
Once connected, your AI client discovers **44 tools** for searching and reading your firm's data, running workflows, and (with the right permissions) authoring workflows. Tools return JSON in a text content block — see the [tools reference](/mcp/tools-reference) for parameters, response shapes, and examples.
| Category | Tools |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Companies | `search_companies`, `list_companies`, `list_company_tags`, `get_company` |
| Custom fields | `get_custom_field_definitions` |
| Documents | `search_documents`, `get_document` |
| People | `search_people`, `get_person` |
| Deals | `search_deals`, `get_deal` |
| Users & teams | `get_current_user`, `search_users`, `get_user`, `list_teams`, `get_team` |
| Lists | `list_lists`, `get_list`, `get_list_entries` |
| Activities | `get_activities`, `get_activity` |
| Screenings & scoring | `search_screenings`, `get_screening`, `get_screening_scores`, `list_scoring_frameworks`, `get_scoring_framework` |
| Workflows (read & run) | `list_workflows`, `get_workflow`, `get_workflow_step`, `list_workflow_runs`, `get_workflow_run`, `get_workflow_run_step`, `run_workflow`, `review_workflow_hitl_step` |
| Workflows (authoring) | `create_workflow`, `update_workflow_details`, `configure_workflow_input`, `configure_workflow_trigger`, `configure_workflow_output`, `add_workflow_step`, `update_workflow_step`, `delete_workflow_step`, `move_workflow_step`, `list_workflow_building_options` |
The server also exposes **resources** (field definitions, document types) and **prompts** (guided research workflows). Details are in the [tools reference](/mcp/tools-reference#resources).
The exact tools available to you depend on your OAuth scopes, organization settings, and app permissions.
## Troubleshooting
Confirm an admin has enabled **Allow access to the Metal MCP server** under **Settings → Organization → API & MCP Access**. Then clear your tool's saved Metal connection and reconnect so it can complete a fresh OAuth sign-in.
Disconnect and reconnect. During sign-in, Metal prompts you to choose an organization when your account belongs to more than one.
Use a bridge like `mcp-remote`. See [other MCP clients](/mcp/clients).
# MCP tools reference
Source: https://docs.metal.ai/mcp/tools-reference
Tool names, parameters, response shapes, and call examples for the Metal MCP server.
This page documents the tools exposed by the Metal MCP server at `https://mcp.metal.ai/mcp`. Your AI client discovers these tools automatically after OAuth sign-in. Use this reference when building integrations, debugging tool calls, or understanding what data comes back.
Tool availability depends on your OAuth scopes and organization settings. If a tool is missing or returns `insufficient scope`, your connection may not have been granted that permission.
## How tool calls work
Metal implements the [Model Context Protocol](https://modelcontextprotocol.io) over streamable HTTP. Your client sends JSON-RPC messages to `/mcp`; each **tools/call** request names a tool and passes a JSON **arguments** object.
### Tool discovery and schemas
After OAuth sign-in, your MCP client discovers the exact live tool metadata from the server with the MCP `tools/list` method. That response is the source of truth for each tool's `description`, `inputSchema`, and safety annotations.
Use this page as the human-readable reference for tool behavior, common parameters, scopes, response shapes, and examples. If you are generating code, validating arguments, or debugging a client integration, inspect the live `inputSchema` from `tools/list` instead of hardcoding this page's parameter tables.
When you inspect a call in a client like Cursor, you typically see:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "search_companies",
"arguments": {
"query": "Acme Industrial",
"pageSize": 5
}
}
```
### Response format
Every tool returns a **text content** block containing JSON (or a plain-text error message). Successful responses fall into three shapes:
**Single record** — the JSON object is the payload directly:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"id": "507f1f77bcf86cd799439011",
"canonicalName": "Acme Industrial",
"sector": "Industrials"
}
```
**Paginated list** — results are wrapped in `data` and `pagination`:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": [
{
"id": "507f1f77bcf86cd799439011",
"canonicalName": "Acme Industrial"
}
],
"pagination": {
"paginationMode": "page",
"page": 1,
"limit": 20,
"resultCount": 1,
"totalCount": 47,
"totalPages": 3,
"hasMore": true
}
}
```
**Document search** — `data` contains grouped documents and excerpts instead of a flat array:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": {
"documents": [
{
"document": {
"id": "507f1f77bcf86cd799439011",
"displayName": "Acme CIM 2024",
"sourceLinkMarkdown": "[Acme CIM 2024 p. 12](https://app.metal.ai/entities/507f1f77bcf86cd799439011)"
},
"excerpts": [
{
"page": 12,
"text": "Revenue grew 18% year-over-year...",
"relevance": 0.92,
"sourceLinkMarkdown": "[Acme CIM 2024 p. 12](https://app.metal.ai/entities/507f1f77bcf86cd799439011)"
}
]
}
],
"citationGuidance": "Always include each excerpt's sourceLinkMarkdown when presenting facts from search_documents."
},
"pagination": {
"paginationMode": "page",
"page": 1,
"limit": 20,
"resultCount": 3,
"documentCount": 1,
"hasMore": false
}
}
```
### Errors
When a call fails validation or the backend returns an error, the tool result is marked as an error and the text content holds a message:
```
query is required for search_companies; use list_companies for filter-only browsing
```
Common error patterns:
| Message | Cause |
| --------------------------------------------- | ----------------------------------------------------------------- |
| `insufficient scope: requires read:companies` | OAuth token lacks the scope for that tool |
| `unauthenticated request` | Session expired or connection not signed in |
| `Rate limit exceeded` | Per-user rate limit hit; JSON body includes `retry_after` seconds |
Rate-limit errors return structured JSON in the error text:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"error": "Rate limit exceeded",
"limit": 60,
"remaining": 0,
"retry_after": 42
}
```
### Pagination
Most list and search tools use **page mode** (`paginationMode: "page"`). Pass `page` (default `1`) and `pageSize` (default `20`, max `50`). Continue while `hasMore` is `true`.
`get_list_entries` uses **cursor mode** (`paginationMode: "cursor"`). Pass `nextToken` from the prior response to fetch the next page.
Field definitions for each resource type are available as MCP resources — see [Resources](#resources) below.
### OAuth scopes
Tools require OAuth scopes granted during sign-in:
| Scope | Tools |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `read:companies` | `list_companies`, `search_companies`, `list_company_tags`, `get_company`, `get_custom_field_definitions` for company fields |
| `read:documents` | `get_document`, `search_documents` |
| `read:people` | `search_people`, `get_person` |
| `read:deals` | `search_deals`, `get_deal`, `get_custom_field_definitions` for deal fields |
| `read:users` | `search_users`, `get_current_user`, `get_user` |
| `read:teams` | `list_teams`, `get_team` |
| `read:lists` | `list_lists`, `get_list`, `get_list_entries` |
| `read:activities` | `get_activities`, `get_activity` |
| `read:workflows` | Workflow read and inspect tools |
| `read:screenings` | `search_screenings`, `get_screening`, `get_screening_scores` |
| `read:scores` | `list_scoring_frameworks`, `get_scoring_framework` |
| `write:workflow_runs` | `run_workflow`, `review_workflow_hitl_step` |
| `write:workflows` | Workflow authoring tools |
***
## Companies
### `search_companies`
Search companies by name. Uses hybrid semantic + keyword matching on company names.
**Scope:** `read:companies`
| Parameter | Type | Required | Description |
| -------------------------- | --------- | -------- | --------------------------------------------------------------------------------------- |
| `query` | string | Yes | Company name or alias to search |
| `sector` | string | No | Filter by sector |
| `subsector` | string | No | Filter by subsector |
| `tagIds` | string\[] | No | Company tag IDs (resolve with `list_company_tags`) |
| `customFilters` | object\[] | No | Org-defined custom company field filters (discover with `get_custom_field_definitions`) |
| `assigneeIds` | string\[] | No | Metal user IDs assigned to the company |
| `assignedToMe` | boolean | No | Restrict to companies assigned to you |
| `assignedTeamIds` | string\[] | No | Team IDs (resolve with `list_teams`) |
| `assignedTeamStatuses` | string\[] | No | Team coverage labels (e.g. Mine/Learn, Monitor) |
| `city`, `state`, `country` | string | No | Location filters |
| `page`, `pageSize` | integer | No | Pagination |
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "search_companies",
"arguments": {
"query": "Acme",
"sector": "Industrials",
"pageSize": 5
}
}
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": [
{
"id": "507f1f77bcf86cd799439011",
"canonicalName": "Acme Industrial",
"sector": "Industrials",
"subsector": "Industrial Automation",
"shortDescription": "Provider of factory automation systems."
}
],
"pagination": {
"paginationMode": "page",
"page": 1,
"limit": 5,
"resultCount": 1,
"hasMore": false
}
}
```
### `list_companies`
Browse companies with structured filters only — no text query. Use this for sector, tag, team, or location lists.
**Scope:** `read:companies`
Same filter parameters as `search_companies` except **`query` is not accepted**. At least one filter is required.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "list_companies",
"arguments": {
"sector": "Healthcare",
"assignedToMe": true,
"pageSize": 20
}
}
```
### `list_company_tags`
List company tags so you can pass exact IDs to `tagIds` filters.
**Scope:** `read:companies`
| Parameter | Type | Description |
| ------------------ | ------- | ------------------- |
| `page`, `pageSize` | integer | Optional pagination |
Returns a paginated list of tag objects with `id` and `name`.
### `get_company`
Get a full company profile by ID.
**Scope:** `read:companies`
| Parameter | Type | Required |
| --------- | ------ | ------------------------------- |
| `id` | string | Yes — 24-character hex ObjectID |
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "get_company",
"arguments": {
"id": "507f1f77bcf86cd799439011"
}
}
```
Returns an enriched company record including `enrichedValues` with citations. Field keys match the `metal://fields/companies` resource.
***
## Custom fields
### `get_custom_field_definitions`
List org-defined custom field definitions for companies and deals. Use this before passing `customFilters` to `search_companies`, `list_companies`, or `search_deals`, and to interpret values in the `custom` map returned by company and deal tools.
**Scope:** `read:companies` for company fields; `read:deals` for deal fields.
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------------------------------------------------------------------------------- |
| `resourceTypes` | string\[] | Optional resource types to return: `companies`, `deals`. Defaults to every supported resource type your token can read. |
Each definition includes the field key, display name, type, whether it can be used in `customFilters`, and supported operators. String fields support `eq`, `neq`, `in`, and `nin`; number fields support `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, and `nin`; boolean fields support `eq` and `neq`; date fields support range operators `gt`, `gte`, `lt`, and `lte`.
***
## Documents
### `search_documents`
Search document content or list documents by type and date.
**Scope:** `read:documents`
| Parameter | Type | Description |
| ------------------ | --------- | ------------------------------------------------------ |
| `query` | string | Content search text. Pass `""` for filter-only listing |
| `documentType` | string | Filter by type (see `metal://document-types`) |
| `companyIds` | string\[] | Restrict to documents linked to these companies |
| `documentIds` | string\[] | Restrict to specific document IDs |
| `sort` | string | `latest` or `oldest` — used when `query` is empty |
| `page`, `pageSize` | integer | Pagination over matching excerpts |
**Content search** — find text inside documents:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "search_documents",
"arguments": {
"query": "unit economics SaaS",
"pageSize": 10
}
}
```
**Filter-only listing** — recent CIMs without relevance ranking:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "search_documents",
"arguments": {
"query": "",
"documentType": "CIM (Confidential Information Memorandum)",
"sort": "latest",
"pageSize": 10
}
}
```
Always cite `sourceLinkMarkdown` from each excerpt when presenting facts.
### `get_document`
Get extracted text from a document by line number.
**Scope:** `read:documents`
| Parameter | Type | Description |
| ----------- | ------- | ------------------------------------------------------- |
| `id` | string | Document ID (required) |
| `startLine` | integer | First line to return (default `1`) |
| `endLine` | integer | Last line (default start + 499; max 500 lines per call) |
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "get_document",
"arguments": {
"id": "507f1f77bcf86cd799439011",
"startLine": 1
}
}
```
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"document": {
"id": "507f1f77bcf86cd799439011",
"displayName": "Acme CIM 2024",
"documentType": "CIM (Confidential Information Memorandum)",
"sourceLinkMarkdown": "[Acme CIM 2024](https://app.metal.ai/entities/507f1f77bcf86cd799439011)"
},
"content": " 1|Executive Summary\n 2|Acme Industrial is a leading...",
"lines": [
{ "line": 1, "text": "Executive Summary", "page": 1 },
{ "line": 2, "text": "Acme Industrial is a leading...", "page": 1 }
],
"startLine": 1,
"endLine": 500,
"returnedLines": 2,
"reachedEof": true,
"status": "EOF - End of document",
"citationGuidance": "When presenting information from this document, include the source markdown link from document.sourceLinkMarkdown."
}
```
When `reachedEof` is `false`, call again with `startLine` set to `nextLine`.
***
## People
### `search_people`
Search contacts by name, title, company, or email.
**Scope:** `read:people`
| Parameter | Type | Description |
| ------------------ | --------- | ----------------------------------------------------------------------- |
| `query` | string | Search text |
| `assigneeIds` | string\[] | Filter by assigned Metal user IDs |
| `assignedToMe` | boolean | Contacts assigned to you |
| `contactType` | string | Advisor, Banker, Board, Executive, Investor, Lender, Operational, Other |
| `priority` | integer | 1 (highest) through 5 (lowest) |
| `page`, `pageSize` | integer | Pagination |
### `get_person`
Get a full contact profile by ID.
**Scope:** `read:people`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
***
## Deals
### `search_deals`
Search or filter deals. `query` is optional for filter-only searches.
**Scope:** `read:deals`
| Parameter | Type | Description |
| ------------------------------- | --------- | ------------------------------------------------------------------------------------ |
| `query` | string | Deal name, company, or description text |
| `companyIds` | string\[] | Target company IDs |
| `sourceCompanyIds` | string\[] | Sourcing bank or advisor company IDs |
| `ownerUserIds` | string\[] | Deal owner user IDs |
| `ownerTeamIds` | string\[] | Deal owner team IDs |
| `status`, `stage` | string | Internal enum or org CRM label |
| `customFilters` | object\[] | Org-defined custom deal field filters (discover with `get_custom_field_definitions`) |
| `createdAfter`, `createdBefore` | string | RFC3339 timestamps |
| `updatedAfter`, `updatedBefore` | string | RFC3339 timestamps |
| `closeAfter`, `closeBefore` | string | RFC3339 timestamps |
| `page`, `pageSize` | integer | Pagination |
Resolve company names with `search_companies`, user names with `search_users`, and team IDs with `list_teams` before passing ID filters.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "search_deals",
"arguments": {
"companyIds": ["507f1f77bcf86cd799439011"],
"status": "Active",
"pageSize": 10
}
}
```
### `get_deal`
Get a full deal record by ID.
**Scope:** `read:deals`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
***
## Users and teams
### `get_current_user`
Return the authenticated user and connected organization. No arguments.
**Scope:** `read:users`
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"user": {
"id": "507f1f77bcf86cd799439011",
"email": "you@firm.com",
"firstName": "Alex",
"lastName": "Smith"
},
"org": {
"id": "507f1f77bcf86cd799439012",
"name": "Example Capital",
"displayName": "Example Capital Partners"
}
}
```
### `search_users`
Search Metal users by name or email. **`query` is required.**
**Scope:** `read:users`
### `get_user`
Get a user by ID.
**Scope:** `read:users`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
### `list_teams`
List teams in your organization. Returns team names and IDs for use in deal and company filters.
**Scope:** `read:teams`
### `get_team`
Get a team by ID.
**Scope:** `read:teams`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
***
## Lists
### `list_lists`
List resource lists in your organization.
**Scope:** `read:lists`
| Parameter | Type | Description |
| ------------------ | ------- | -------------------------------------------------- |
| `resourceType` | string | Optional filter: `company`, `person`, `deal`, etc. |
| `page`, `pageSize` | integer | Pagination |
### `get_list`
Get a list's metadata and column schema by ID. Does not include row data — use `get_list_entries`.
**Scope:** `read:lists`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
### `get_list_entries`
Page through list rows with cursor pagination.
**Scope:** `read:lists`
| Parameter | Type | Description |
| ----------- | ------- | ---------------------------------- |
| `id` | string | List ID (required) |
| `pageSize` | integer | Rows per page (default 50, max 50) |
| `nextToken` | string | Token from prior response |
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": [
{
"id": "entry-id",
"resourceId": "507f1f77bcf86cd799439011",
"values": { "Priority": "High" }
}
],
"pagination": {
"paginationMode": "cursor",
"limit": 50,
"resultCount": 1,
"hasMore": true,
"nextToken": "eyJwYWdlIjoyfQ=="
}
}
```
***
## Activities
### `get_activities`
Get meetings, calls, and interactions for a company.
**Scope:** `read:activities`
| Parameter | Type | Required | Description |
| ------------------ | ------- | -------- | ----------- |
| `participantId` | string | Yes | Company ID |
| `page`, `pageSize` | integer | No | Pagination |
### `get_activity`
Get a single activity by ID, including participants, linked deals, and attached documents.
**Scope:** `read:activities`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
***
## Screenings and scoring
### `search_screenings`
List or filter CIM screenings. Free-text `query` is not supported — use structured filters.
**Scope:** `read:screenings`
| Parameter | Type | Description |
| ---------------------------------------- | --------- | ------------------------------------------------------------ |
| `companyIds`, `dealIds`, `dataEntityIds` | string\[] | Link filters |
| `status`, `statuses` | string | Screening status |
| `sector`, `subsector`, `industry` | string | Taxonomy filters |
| `createdAfter`, `createdBefore` | string | RFC3339 |
| `updatedAfter`, `updatedBefore` | string | RFC3339 |
| `sortBy` | string | `createdAt`, `updatedAt`, `companyName`, `revenue`, `ebitda` |
| `sortOrder` | string | `asc` or `desc` |
| `page`, `pageSize` | integer | Pagination |
### `get_screening`
Get a screening by ID, including financials, analysis, and validation metadata.
**Scope:** `read:screenings`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
### `get_screening_scores`
Get score records for a screening.
**Scope:** `read:screenings` and `read:scores`
| Parameter | Type | Description |
| ------------- | ------ | --------------------------------- |
| `screeningId` | string | Required |
| `framework` | string | Optional filter by framework name |
### `list_scoring_frameworks`
List scoring frameworks visible to your organization.
**Scope:** `read:scores`
| Parameter | Type | Description |
| ----------------- | ------- | ------------------------------------------- |
| `entityType` | string | `company`, `person`, `deal`, or `screening` |
| `includeStandard` | boolean | Append standard frameworks |
| `onlyStandard` | boolean | Return only standard frameworks |
### `get_scoring_framework`
Get a framework definition by ID. Standard frameworks use IDs like `standard:Porter's Five Forces`.
**Scope:** `read:scores`
| Parameter | Type | Required |
| --------- | ------ | -------- |
| `id` | string | Yes |
***
## Workflows
Workflow tools split into **read/inspect**, **run**, and **authoring** groups.
### Read and inspect
**Scope:** `read:workflows`
| Tool | Purpose |
| ----------------------- | -------------------------------------------------------------------------------------- |
| `list_workflows` | Discover workflow IDs (`page`, `pageSize`) |
| `get_workflow` | Workflow definition overview (`workflowId`, optional `version`, `fieldPath`) |
| `get_workflow_step` | Full step definition (`workflowId`, `stepId`, optional `childStepId`, `version`) |
| `list_workflow_runs` | Find runs (`resourceType`, `resourceId`, `statuses`, date filters, pagination) |
| `get_workflow_run` | Run status and outputs (`workflowRunId`, `view` or `fieldPath`) |
| `get_workflow_run_step` | Step evidence (`workflowRunId`, `stepId`, optional `itemIndex`, `childStepId`, `view`) |
`view` shortcuts for `get_workflow_run`: `overview`, `steps`, `outputs`, `errors`, `failures`, `artifacts`, `citations`, `full`. Do not pass `view` and `fieldPath` together.
`view` shortcuts for `get_workflow_run_step`: `overview`, `outputs`, `transcript`, `toolCalls`, `artifacts`, `errors`, `iteratorItems`, `hitl`, `execution`, `full`.
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "get_workflow",
"arguments": {
"workflowId": "507f1f77bcf86cd799439011",
"fieldPath": "inputSchema"
}
}
```
### Run workflows
**Scope:** `write:workflow_runs`
#### `run_workflow`
Start a workflow run. Inspect `inputSchema` with `get_workflow` first and supply every required field in `input`.
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | -------------------------------------- |
| `workflowId` | string | Yes | 24-character hex ID |
| `input` | object | No | Workflow inputs matching `inputSchema` |
| `visibility` | string | No | Run visibility override |
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"name": "run_workflow",
"arguments": {
"workflowId": "507f1f77bcf86cd799439011",
"input": {
"companyId": "507f1f77bcf86cd799439012"
}
}
}
```
The response includes immediate run status and polling guidance. Call `get_workflow_run` when the run completes to read bounded outputs.
#### `review_workflow_hitl_step`
Approve or reject a human-in-the-loop step. Inspect the pending step with `get_workflow_run_step` using `view: "hitl"` first.
| Parameter | Type | Required | Description |
| ----------------- | --------- | -------- | --------------------------------------------------- |
| `workflowRunId` | string | Yes | Run ID |
| `stepId` | string | Yes | HITL step ID |
| `action` | string | Yes | `approve` or `reject` |
| `approvedItemIds` | string\[] | No | Required item IDs when approving structured reviews |
### Authoring
**Scope:** `write:workflows`
These tools create and edit draft workflows:
| Tool | Purpose |
| -------------------------------- | ---------------------------------------------------------- |
| `create_workflow` | Create a new draft workflow |
| `update_workflow_details` | Update name, description, and metadata |
| `configure_workflow_input` | Set input schema and UI schema |
| `configure_workflow_trigger` | Configure how the workflow starts |
| `configure_workflow_output` | Configure output sections |
| `add_workflow_step` | Add a step to the definition |
| `update_workflow_step` | Update an existing step |
| `delete_workflow_step` | Remove a step |
| `move_workflow_step` | Reorder steps |
| `list_workflow_building_options` | List valid step types, triggers, and configuration options |
Call `list_workflow_building_options` before authoring to discover allowed step types and field shapes. Authoring tool inputs are validated server-side; validation errors return structured JSON in the error text.
***
## Resources
Your client can read MCP **resources** alongside tools. These are read-only catalogs — not tool calls.
| URI | Description |
| --------------------------- | -------------------------------------------------- |
| `metal://document-types` | Valid `documentType` values for `search_documents` |
| `metal://resource-types` | Entity types, associated tools, and relationships |
| `metal://fields/companies` | Field keys returned by company tools |
| `metal://fields/people` | Field keys returned by people tools |
| `metal://fields/deals` | Field keys returned by deal tools |
| `metal://fields/documents` | Field keys returned by document tools |
| `metal://fields/activities` | Field keys returned by activity tools |
## Prompts
The server also exposes guided **prompts** your client can invoke:
| Prompt | Arguments |
| ----------------------- | ---------------------------------------- |
| `company_due_diligence` | `company_name` (required), `focus_areas` |
| `deal_analysis` | `deal_name` (required) |
| `sector_landscape` | `sector` (required), `region` |
| `document_research` | `topic` (required), `document_type` |
| `contact_network` | `company_name` (required), `role_filter` |
Prompts orchestrate multiple tool calls — they do not return data directly.
***
## Tool index
`add_workflow_step` · `configure_workflow_input` · `configure_workflow_output` · `configure_workflow_trigger` · `create_workflow` · `delete_workflow_step` · `get_activities` · `get_activity` · `get_company` · `get_current_user` · `get_custom_field_definitions` · `get_deal` · `get_document` · `get_list` · `get_list_entries` · `get_person` · `get_scoring_framework` · `get_screening` · `get_screening_scores` · `get_team` · `get_user` · `get_workflow` · `get_workflow_run` · `get_workflow_run_step` · `get_workflow_step` · `list_companies` · `list_company_tags` · `list_lists` · `list_scoring_frameworks` · `list_teams` · `list_workflow_building_options` · `list_workflow_runs` · `list_workflows` · `move_workflow_step` · `review_workflow_hitl_step` · `run_workflow` · `search_companies` · `search_deals` · `search_documents` · `search_people` · `search_screenings` · `search_users` · `update_workflow_details` · `update_workflow_step`
# Quickstart
Source: https://docs.metal.ai/quickstart
Create an API key and make your first request to the Metal API.
This guide takes you from zero to your first successful API response. You'll create an API key, authenticate a request, and create your first company.
## Prerequisites
Before you begin, you need:
* A Metal account with access to your organization's workspace
* Permission to create API keys (an organization admin can grant this)
* A terminal with `curl`, or any HTTP client
## Get started
API keys are scoped to your organization. Create one in the Metal app:
1. Go to **Settings → Organization → API & MCP Access** (you need an admin role).
2. In the **API Keys** card, click **Create API Key**.
3. Enter a **Name** and click **Create**.
4. Copy the **Client ID** and **API Key** that Metal displays.
Each key has two parts:
* **Client ID**: sent in the `x-metal-client-id` header
* **API Key**: sent in the `x-metal-api-key` header
The API key secret is shown only once, at creation time. Store it in a secret manager and never commit it to source control.
Export your credentials so the examples below can read them.
```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export METAL_CLIENT_ID="ci_your_client_id_here"
export METAL_API_KEY="pk_your_api_key_here"
```
List the companies in your organization to confirm your credentials work.
```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.metal.ai/v1/companies \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY"
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import requests
headers = {
"x-metal-client-id": os.environ["METAL_CLIENT_ID"],
"x-metal-api-key": os.environ["METAL_API_KEY"],
}
res = requests.get("https://api.metal.ai/v1/companies", headers=headers)
res.raise_for_status()
print(res.json()["data"])
```
```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const res = await fetch("https://api.metal.ai/v1/companies", {
headers: {
"x-metal-client-id": process.env.METAL_CLIENT_ID,
"x-metal-api-key": process.env.METAL_API_KEY,
},
});
const { data } = await res.json();
console.log(data);
```
A successful response wraps the result in a `data` field:
```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
"data": [
{
"id": "665f1c2a9b1e4a0012a3b4c5",
"canonicalName": "Acme Industrials",
"website": "https://acme.example",
"createdAt": "2026-06-01T12:00:00Z"
}
]
}
```
Now write some data. The only required field is `canonicalName`.
```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.metal.ai/v1/companies \
-H "x-metal-client-id: $METAL_CLIENT_ID" \
-H "x-metal-api-key: $METAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "canonicalName": "Northwind Components" }'
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
res = requests.post(
"https://api.metal.ai/v1/companies",
headers={**headers, "Content-Type": "application/json"},
json={"canonicalName": "Northwind Components"},
)
res.raise_for_status()
print(res.json()["data"])
```
```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const res = await fetch("https://api.metal.ai/v1/companies", {
method: "POST",
headers: {
"x-metal-client-id": process.env.METAL_CLIENT_ID,
"x-metal-api-key": process.env.METAL_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ canonicalName: "Northwind Components" }),
});
const { data } = await res.json();
console.log(data);
```
## Next steps
Manage keys, headers, and the scopes available to your token.
Page through large collections with `page`, `limit`, and `lt`.
Enrich a company with AI-sourced firmographics and financials.
Browse every endpoint with live examples.
Stuck on a request? Email [support@metal.ai](mailto:support@metal.ai) with the method, path, and the error body you received.