<!-- Cogny documentation. Canonical page: https://cogny.com/docs/connect-your-own-app-to-automations -->

# Connect Your Own App to Cogny Automations

Expose your app's data and actions as an MCP endpoint, register it with Cogny, and run a scheduled agent against it. The full loop a coding agent can do end to end with the Cogny CLI.

**Author:** Cogny Team  
**Published:** 2026-09-30  
**Updated:** 2026-09-30  
**Canonical:** https://cogny.com/docs/connect-your-own-app-to-automations

Cogny automations are scheduled agents that act through tools you have greenlit. Most of those tools come from Cogny's own integrations: Google Ads, Meta, GA4, Discord, and so on. When the data or the action lives in your own app (a booking system, a member database, an internal admin), you expose it as an MCP endpoint, register it with Cogny, and the automation calls it like any other integration.

This guide is written for a coding agent or developer working in the user's codebase. Everything below can be done from the terminal with the Cogny CLI. The dashboard covers the simpler cases (one tool per job); see the end of this page.

## When to use this

- Your app holds data Cogny can't reach through a built-in integration, and a weekly or daily agent should act on it.
- The action needs rules only your app can enforce: consent, opening hours, per-customer limits, an approval queue.
- You want every run recorded as a ticket, with a transcript of what the agent read and did.

If you only need a nightly data sync with no judgement involved, use Scripts instead ([Automations and content loops](/docs/automations-and-content-loops#scripts)).

## The example: a retention agent for a membership business

A martial-arts school, gym or dance studio has the same pattern: members who miss two weeks tend to quit. The agent runs once a week. It finds members who have not been seen in 14 days, books each a make-up class, and texts the family in the instructor's voice.

The app exposes six tools:

| Tool | Kind | What it does |
|---|---|---|
| `get_retention_report` | read | At-risk members with last visit, recent attendance, contact, text consent and make-up options |
| `get_student` | read | One member's profile, notes, history and messages already sent |
| `list_class_schedule` | read | Upcoming classes with ids and dates |
| `book_makeup_class` | write | Books a member into a class on a date |
| `send_parent_text` | write | Texts the family, or queues the text for staff approval |
| `log_parent_message` | write | Records a call, email or note |

## 1. Log in to the user's workspace

Ask the user for an API key: Settings → Data & Integrations → **Connect AI Clients**, **cogny cli** tab. The key is shown once. The user already has a workspace, so don't run `cogny init`; that creates a new one.

```bash
alias cogny="npx @cogny/cli"
cogny login --api-key <key>
cogny status          # subscription, credits, connected integrations
cogny tools list      # automation tools appear only if automations are on
```

Automations are included with Cloud. If `create_automation` is missing from `cogny tools list`, automations aren't switched on for this workspace; tell the user to contact Cogny support.

## 2. Build the MCP endpoint

Cogny talks to your endpoint over Streamable HTTP (JSON-RPC over POST) and sends `Authorization: Bearer <api_key>` on every call. A minimal Next.js route with `mcp-handler` and zod:

```ts
// app/api/agent/mcp/route.ts
import crypto from 'node:crypto';
import { createMcpHandler } from 'mcp-handler';
import { z } from 'zod';

const handler = createMcpHandler((server) => {
  server.registerTool('get_retention_report', {
    description: 'Members not seen for 14+ days, with make-up options for the next 7 days.',
    inputSchema: z.object({ at_risk_only: z.boolean().default(true) }),
    annotations: { readOnlyHint: true },
  }, async ({ at_risk_only }) => json(await retentionReport(at_risk_only)));

  server.registerTool('send_parent_text', {
    description: 'Text a family. Refused without consent or outside 8am–9pm local time.',
    inputSchema: z.object({ student_id: z.string().uuid(), message: z.string().max(300) }),
    annotations: { readOnlyHint: false, destructiveHint: false },
  }, async (input) => json(await sendText(input))); // consent, quiet hours, opt-out line enforced here
});

// Constant-time compare, so the key can't be guessed from response timing.
function validKey(req: Request) {
  const key = process.env.AGENT_API_KEY;
  const got = (req.headers.get('authorization') || '').replace(/^Bearer\s+/i, '');
  if (!key) return false;
  const a = Buffer.from(got), b = Buffer.from(key);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

async function authed(req: Request) {
  if (!validKey(req)) return new Response('Unauthorized', { status: 401 });
  return handler(req);
}
export { authed as GET, authed as POST };
```

`json`, `retentionReport` and `sendText` stand for your own code. Generate the key with `openssl rand -hex 32`, set it on the server, and deploy.

Two rules decide what a run can do:

- **Read tools must be recognisable.** Unless you list read tools yourself, a run is offered a tool on a granted server when its name starts with `get`, `list`, `search`, `execute`, `inspect`, `run`, `fetch`, `query`, `read`, `describe`, `find`, `check`, `count`, `lookup`, `view` or `show` (a leading `tool_` is ignored). A tool annotated `readOnlyHint: true` also counts, unless it is also `destructiveHint: true`. A tool called `retention_report` with no annotation is left out, and the agent can't see it. Annotate it, rename it, or list it in `read_tools` (step 4).
- **Safety rules go in the write tools, not the prompt.** Check consent, quiet hours, rate limits and opt-out inside `send_parent_text`, and return a clear error when a call is refused. The agent reads the error and moves on. A prompt can be misread; your endpoint can't be talked out of a rule. If staff should see messages before they go out, make the write tool queue a draft for approval in your admin rather than send.

## 3. Register the endpoint

```bash
cogny tools call add_custom_mcp_server --input '{
  "name": "Dojo",
  "server_url": "https://example-dojo.com/api/agent/mcp",
  "api_key": "<AGENT_API_KEY>",
  "description": "Members, attendance, make-up classes and family texts"
}'
```

The result has a `server_id` and every discovered tool with `read_only: true|false`. `false` means the tool wasn't recognised as read-only, not that it must be a write. A tool that really only reads but shows `false` can go in `read_tools`; a write tool goes in `action_grants`. Only owners and admins can register servers, and the URL must be `https`. Call it again with the same name to change the URL or rotate the key.

After you deploy changes to the endpoint, run `cogny tools call refresh_mcp_discovery --input '{"server_name":"Dojo"}'` so Cogny sees the new tool list.

In the dashboard, the same step is Settings → **MCPs For Cogny** → MCP Servers → **Add Server**, with auth type **API key**.

## 4. Create the automation, disabled

```bash
cogny tools call create_automation --input '{
  "name": "Weekly retention",
  "crontab": "0 10 * * 1",
  "timezone": "America/Indiana/Indianapolis",
  "enabled": false,
  "instructions": "Call get_retention_report. For each at-risk member: read get_student, skip anyone whose notes say not to contact or who was contacted this week, book one make-up class with book_makeup_class, then send_parent_text if they have text consent (warm, under 300 characters, first name, mention the class day and time). Without consent, log_parent_message a note that a call is needed. End with a summary: who was contacted, what was booked, who needs a call.",
  "action_grants": [
    { "server_name": "Dojo", "tool_name": "book_makeup_class" },
    { "server_name": "Dojo", "tool_name": "send_parent_text" },
    { "server_name": "Dojo", "tool_name": "log_parent_message" }
  ]
}'
```

- **action_grants** are the write tools the run may call, one entry each. `mcp_server_id` is optional and is looked up from `server_name`. Add `constraints` to pin an input field to a fixed value. For example, `{"channel_id": "123"}` locks a Discord post to one channel. A call with a different value is refused at call time.
- **read_tools** (optional) is an explicit list of read tools, `[{ "server_name": "Dojo", "tool_name": "retention_report" }]`. Once set, it replaces the automatic read-tool selection. Some tools are refused here and have to be granted instead:
  - tools annotated as writes (`readOnlyHint: false` or `destructiveHint: true`)
  - tools whose name starts with a write verb (`create`, `update`, `delete`, `send`, `post`, …), unless annotated `readOnlyHint: true`
- **timezone** is an IANA name. Without it, the crontab runs on Europe/Stockholm time.

The result includes `pre_approved_tools`: the exact tool list every run gets. Nothing else is offered to the agent. `list_automations` shows the same list later.

## 5. Dry-run and inspect

```bash
cogny tools call run_automation --input '{"automation_id":"<id>","dry_run":true}'
cogny tools call get_automation_run --input '{"ticket_id":"<ticket_id>"}'
```

A dry run has no write authority. The agent reads, then reports what it would have booked and sent. `get_automation_run` returns the run status, any error, and a condensed transcript of each tool call with its input and a preview of the result. Fix the endpoint or the instructions and dry-run again until the report reads right. Dry runs use credits like any run.

## 6. Run once for real, then enable

Show the user the dry-run summary and get their OK. Then:

```bash
cogny tools call run_automation --input '{"automation_id":"<id>","dry_run":false}'
cogny tools call update_automation --input '{"automation_id":"<id>","enabled":true}'
```

`update_automation` only changes the fields you pass. `action_grants` and `read_tools` replace the whole list, and `read_tools: null` goes back to automatic read-tool selection. Enabling needs a schedule: `enabled: true` fails with "Set a crontab before enabling" until the automation has a `crontab`. `run_automation` works without one.

## What a run looks like

Every run, scheduled or run now, becomes a ticket on the Tickets board with its full transcript, so the user can see what the agent read, booked and sent. Runs use the workspace's credits like any agent run. When the workspace is out of credits, the run is skipped instead of starting. A schedule fires at most once an hour.

## Common mistakes

- **Wrong timezone.** `0 10 * * 1` without a `timezone` runs at 10:00 Stockholm time, which is 04:00 in Indiana. A quiet-hours rule then refuses every text. Always pass the business's timezone.
- **Invisible read tools.** A read tool named outside the prefix list and not annotated `readOnlyHint: true` is not offered. Check `read_only` in the `add_custom_mcp_server` result and `pre_approved_tools` after creating.
- **Consent only in the prompt.** If the prompt is the only thing stopping a text to a family without consent, a misread prompt sends it. Enforce it in the write tool.
- **Unpinned grants.** A grant on a tool that takes a target (a channel, an account, a campaign) without `constraints` lets the agent choose any target. Pin it.
- **Stale tool list.** After deploying a new or renamed tool, run `refresh_mcp_discovery`. Otherwise Cogny still has the old list and the new tool is missing from the run.
- **Automations off.** If the automation tools don't appear, automations aren't switched on for the workspace. They're included with Cloud; contact Cogny support.

## In the dashboard

Reports → Create new → **New Automation** creates a job with one allowed tool, plus one optional pinned field. It is created disabled, on a Monday-morning schedule in your browser's timezone. After that, the automation's **Settings** dialog has the schedule picker with a **Timezone** selector. It also lets you edit each grant's label and pinned fields, or remove extra grants. **Run now** on the automation page is a real run, the same as `run_automation` with `dry_run: false`. Use the tool for dry runs.

Adding more write tools, or setting `read_tools`, isn't in the dashboard. Do it through the in-app chat or with `create_automation` / `update_automation` over MCP or the CLI.

Related: [Automations and content loops](/docs/automations-and-content-loops) · [Use Cogny from your own agent](/docs/bring-your-own-agent) · [Cogny MCP server](/docs/mcp)

---

## Steps: Run a scheduled Cogny automation against your own app

1. **Log in to the user's workspace** — Ask the user for an API key from Settings → Data & Integrations → Connect AI Clients, then run npx @cogny/cli login --api-key <key> and cogny status.
2. **Build an MCP endpoint in the app** — Serve Streamable HTTP MCP behind a Bearer token. Name read tools get_/list_/search_ or annotate them readOnlyHint: true. Put consent and safety rules inside the write tools.
3. **Register the endpoint** — Call add_custom_mcp_server with name, server_url and api_key. Check that every tool's read_only flag is what you expect.
4. **Create the automation disabled** — Call create_automation with instructions, crontab, an IANA timezone, and one narrow action_grant per write tool, with constraints where a target should be fixed.
5. **Dry-run and inspect** — Call run_automation with dry_run true, then get_automation_run on the returned ticket_id. Fix the endpoint or instructions and repeat.
6. **Run once for real, then enable** — With the user's OK, run_automation with dry_run false, check the result, then update_automation with enabled true.

---

In this section:

- Set Up Cogny Cloud for Success: https://cogny.com/docs/cloud-setup-guide
- Before Your Onboarding: Checklist and Wizard: https://cogny.com/docs/cloud-onboarding-checklist
- Connect Your Data Sources: https://cogny.com/docs/connect-your-data-sources
- Context and Growth Strategy: https://cogny.com/docs/context-and-growth-strategy
- Core Metrics: https://cogny.com/docs/core-metrics
- Reports and Templates: https://cogny.com/docs/reports-and-templates
- Tickets and Approvals: https://cogny.com/docs/tickets-and-approvals
- Automations and Content Loops: https://cogny.com/docs/automations-and-content-loops
- **Connect Your Own App to Cogny Automations** (this page): https://cogny.com/docs/connect-your-own-app-to-automations
- Coding Agent, GitHub and Cogny Sites: https://cogny.com/docs/coding-agent-and-cogny-sites
- Use Cogny from Your Own Agent: https://cogny.com/docs/bring-your-own-agent
- Credits and Plans: https://cogny.com/docs/credits-and-plans
- Team, Governance and Privacy: https://cogny.com/docs/team-governance-and-privacy
- Cogny Cloud FAQ: https://cogny.com/docs/cloud-faq

Source page: https://cogny.com/docs/connect-your-own-app-to-automations  
All documentation: https://cogny.com/docs  
Cogny for agents: https://cogny.com/llms.txt
