---
title: Set up an action
description: Add any of the catalog's actions to an agent, choose who performs it, and connect its API the way that API takes its key.
---
An **action** is one thing an agent may do: send a Gmail message, create a
Trello card, refund a Stripe charge. Each becomes a **tool** on the agent, and
every call to it runs Veefore's policy, risk and approval first. This page is
how to set one up. The [actions catalog](/reference/actions) lists every
action, app by app, with the exact call and what to enter.

## The steps

:::steps
:::step Add it
**Integrations → n8n**, choose the agent, then **Add tools**:

- **Browse apps**: pick an app, or search ("trello create card", "stripe
  refund"). No model is involved.
- **Describe the agent**: Veefore's model picks actions from the catalog. It
  only chooses; it cannot invent a field or an endpoint.
- **Import OpenAPI**: any API with an OpenAPI 3 or Swagger 2 document.

Tick the actions and **Add**. Each joins the list as a row.
:::
:::step Choose who performs it
Each row has a switch: **n8n** or **Veefore**.

- **n8n performs it**: Veefore decides; the n8n node you connect to the
  Veefore node's **Allowed** output does the work, with the credential you
  keep in n8n. The row names that credential and links n8n's page on it.
- **Veefore performs it**: Veefore decides and then makes the call itself, with
  a key sealed in its broker. n8n never holds the key, and a refused call
  cannot happen.

Apps that are not a web API (databases, queues, file transfer, local steps),
or whose requests Veefore cannot sign, offer only n8n. The row says why.
:::
:::step Enter the key
For **Veefore performs it**, the row asks for what it still needs. One value
(a token, an API key) fits in the row; **Get a … key** opens the provider page
where it is issued. When an API needs more than one value (a username and
password, a token and an account ID, an OAuth client), the row says what and
**Enter them** opens the editor.

The endpoint is already filled in. If its host has a `{placeholder}`
(`https://{subdomain}.zendesk.com/…`), **Edit endpoint** and put in your own.
:::
:::step Save and try it
**Save** creates the tools. **Try a decision** runs your policy against a
sample call without performing it. Then connect n8n with the Key ID and
connection secret ([n8n](/build/n8n)).
:::
:::

## Where each endpoint comes from

The endpoint is the call n8n itself makes for that action, so "Veefore performs
it" does exactly what the n8n node would:

| Source | For |
| --- | --- |
| The provider's OpenAPI document | The API actions (Claude, Gemini, Groq, Resend, Stripe, Twilio, SendGrid, Slack, Discord, Asana, GitHub, Intercom, Brevo) and anything you import. Fields, types, required values and placement are the spec's, and every call is checked against them before it is sent. |
| n8n's node definition | Declarative nodes, which state the method and URL in data. |
| n8n's node source | Nodes that make their calls in code. The source is read without running it: the method, the URL template and which field fills each `{placeholder}`. |
| Reviewed per action | Calls built at run time (Microsoft Teams meetings, Excel workbooks, Odoo, the AWS services, GraphQL APIs). Each was written from that action's code in n8n and the provider's reference. |

Where none of these gives an exact call, the action is **n8n performs it**
only, rather than a guessed endpoint.

:::note n8n's field names
An action from n8n's catalog carries the n8n node's fields under n8n's names
(`sendTo`, `subject`). When Veefore performs it, those fields are what it
sends, and some APIs name them differently. If the provider rejects a call
for a field, use the provider's API action from the catalog (its fields are
the API's own, and checked) or import its OpenAPI document.
:::

## How the key is sent

Each app takes its key in its own way, and the catalog knows which. You only
enter the values; Veefore places them. The [catalog](/reference/actions) shows
it for every app.

| The API takes | You enter | For example |
| --- | --- | --- |
| A bearer token (`Authorization: Bearer …`) | The token | Notion, Matrix, OpenAI, Coda |
| A key in a header, maybe with a prefix | The key | Anthropic (`x-api-key`), DeepL (`DeepL-Auth-Key …`), PagerDuty (`Token token=…`), MessageBird (`AccessKey …`) |
| A key in the query string | The key | Pipedrive (`?api_token=`), Hunter, NASA, OpenWeatherMap, Yourls |
| A key in the request body | The key | UptimeRobot, Vonage, Mandrill, Paddle, PostHog |
| A key in the URL path | The key | Telegram (`/bot<token>/…`), SIGNL4 |
| Username and password (basic auth) | What the API calls them | Zendesk (your email followed by `/token`, and the API token), Jira, WooCommerce |
| Basic auth with the key alone | The key | Chargebee and Onfleet (key as username), Lemlist and Affinity (key as password), Freshservice (password fixed to `X`) |
| OAuth 2.0 | The OAuth app's client ID and secret, and a refresh token | Gmail, Microsoft Teams and Outlook, Webflow, Google Sheets |
| An AWS access key | Access key ID, secret access key, region | Every AWS action |

### Values beside the key

Some APIs want a second value with every call. The editor asks for it and
says where it goes:

| App | Beside the key |
| --- | --- |
| Harvest | **Account ID**, in the `Harvest-Account-Id` header |
| Trello | **API token**, in the query beside the key |
| Discourse | **Username**, in `Api-Username` |
| Quick Base | **Realm hostname**, in `QB-Realm-Hostname` |
| Netscaler ADC, RocketChat, Salesmate, Demio, Vonage, Mocean, Paddle | A user, user ID, domain, secret or vendor ID |

Fixed values (Copper's `X-PW-Application: developer_api`) and a key the API
wants twice (Supabase's `apikey` beside its bearer token) need nothing from
you; the editor shows what else is sent.

### OAuth

Veefore keeps the **refresh token** and renews access tokens itself, so the
tool keeps working after the first one expires. Create an OAuth app with the
provider, authorize it once with offline access, and paste the client ID,
client secret and refresh token. The token URL and scope come from n8n's
definition of that app's OAuth credential. For Google, Google's OAuth
Playground gives a refresh token for your own client.

### AWS

Veefore signs each request with Signature Version 4. The region you choose
moves the endpoint (`dynamodb.eu-west-1.amazonaws.com`). JSON APIs (DynamoDB,
Cognito, Comprehend, Transcribe, Certificate Manager, Textract, Rekognition)
name the operation in `X-Amz-Target`; query APIs (IAM, SES, SNS, SQS, ELB)
take `Action` and `Version` in the URL with the fields form-encoded. S3 uses
path-style URLs (`s3.us-east-1.amazonaws.com/<bucket>/<key>`). Use an IAM user
or role limited to what the agent needs.

## Special shapes

| Shape | What to know |
| --- | --- |
| GraphQL APIs (Monday.com, Linear, Emelia) | One endpoint. The tool takes a `query` document and its `variables`, so the agent writes the GraphQL for the action. |
| Your own host | Self-hosted and per-account apps (Zendesk, Mautic, NocoDB, Odoo, Nextcloud) have `{subdomain}` or `{host}` in the endpoint. Put in yours; a placeholder can never stay in the host. |
| Path fields | `{field}` in the path is filled from the field of that name, encoded, so a value can never change the host or add a segment. |
| Fixed protocol headers | Headers an API needs on every call (`Notion-Version`, `anthropic-version`, `Travis-API-Version`) are sent for you. They can never carry or replace a credential. |

## What Veefore guarantees about the key

- It is sealed in the broker when you save, and never shown or returned again.
- It is placed only after the call's host has passed the tool's destination
  check, and redirects are refused, so a key reaches only its own API.
- A field the caller sends with the same name as the key (`?key=…`) is
  replaced by the key, never the other way round.
- A credential can never set the `Host`, framing or Veefore's own headers, a
  line break in a header, or the same value twice. Such a credential is
  refused when it is saved.
- The key and the values beside it never appear in a response, a log or the
  audit trail.

## Only n8n can perform these

| Why | Apps |
| --- | --- |
| Not a web API | Postgres, MySQL, MongoDB, Redis, Kafka, RabbitMQ, MQTT, FTP, Send Email (SMTP), Crypto, JWT, TOTP and other local steps |
| Each request is signed with another scheme | Azure Storage, Azure Cosmos DB (shared key), Unleashed (HMAC), S3-compatible servers |
| The key is first exchanged for a short-lived token | Bitwarden, HaloPSA, PayPal, Taiga, SeaTable, FileMaker, Google service accounts |
| The action needs several calls | "Send and wait for a reply" in Gmail, Slack, Telegram and others; Excel and Google Sheets update and upsert |
| The call has a shape Veefore does not send | Nextcloud files (WebDAV), Firebase Realtime Database paths, Todoist reminders (Sync API command batches), file uploads |

The full list, with the reason for each, is in the
[catalog index](/reference/actions#only-n8n-can-perform-these-apps).

## Troubleshooting

| Symptom | Cause |
| --- | --- |
| Saving: "A credential cannot set the … header" | The header is reserved (Host, Content-Length, Veefore's own). Use the header the API documents. |
| Saving: "… is placed twice" | Two values go to the same header or parameter. Remove one. |
| The call fails with the provider's `401` | The key is wrong, or for basic auth the username and password are swapped. The [catalog](/reference/actions) says which value goes where for each app. |
| "This credential goes in the request body, and a GET request has none" | The API takes its key in the body, and the endpoint was changed to GET. Use the method the catalog lists. |
| The provider rejects a field | n8n's field names differ from the API's. Use the provider's API action or import its OpenAPI document. |
| OAuth: `invalid_grant` | The refresh token was revoked or was issued for another client. Authorize again with offline access. |
| AWS: `SignatureDoesNotMatch` | The secret access key is wrong, or the region does not match the endpoint. |

## Related

:::cards
:::card Actions catalog | /reference/actions
Every app and action, with the call, the key and what to enter.
:::
:::card n8n | /build/n8n
Install the node and connect the instance.
:::
:::card Broker a credential | /build/broker-a-credential
The credential formats behind each way of sending a key.
:::
:::
