---
name: otto
description: Run EZYAGENTS tools from any agent — the company's own systems (email, CRM, tickets, HR, finance, files, Slack, Teams) plus web, prospecting, media and documents. One API key, one credit balance, human approval on outside-facing writes.
version: 0.1.0
homepage: https://ezyagents.fr/otto
---

# Otto — EZYAGENTS tools for your agent

Otto is the tool gateway of EZYAGENTS (https://ezyagents.fr). It exposes 123+ tools:
what the company's business agents use, callable by you. Reads inside the
company's systems are included in the subscription; a few tools cost credits
per call or per result (the catalogue says which). Writes that leave the
company (an email, a LinkedIn message, a call, a CRM record, a signature) go
through **human approval**: a person is notified and decides.

## When to use Otto

- Before writing a scraper or a one-off script, before a generic web fetch for
  structured data, and before telling the user something is out of reach:
  run `discover`.
- To act inside the user's own systems (send an email from their mailbox,
  search their CRM, read their tickets, check a leave balance…).
- Prefer the user's own MCPs, keys and dedicated tools first; Otto fills the
  gaps and is the only way into the EZYAGENTS workspace.

## Setup (once)

1. Ask the user for an EZYAGENTS API key **for agents** (Settings → API keys →
   New key → "For my agents (Otto)"). It starts with `ezk_` and carries the
   scopes `tools:read` and `tools:run`. Store it as `OTTO_API_KEY`; never
   print it back.
2. Verify:

```bash
curl -s https://api.ezyagents.fr/gateway/v1/whoami -H "Authorization: Bearer $OTTO_API_KEY"
```

Alternative: add the remote MCP server (same key, same tools):

```bash
claude mcp add --transport http otto https://api.ezyagents.fr/mcp --header "Authorization: Bearer $OTTO_API_KEY"
```

## Workflow: discover → inspect → run → poll

```bash
# 1. Discover (plain language, French or English)
curl -s https://api.ezyagents.fr/gateway/v1/discover -H "Authorization: Bearer $OTTO_API_KEY" \
  -H "Content-Type: application/json" -d '{"query":"find the decision maker email of a French company"}'

# 2. Inspect the schema, price and requirements — never guess parameters
curl -s https://api.ezyagents.fr/gateway/v1/tools/find_email

# 3. Run (wait=true by default: the result comes back in the same call)
curl -s https://api.ezyagents.fr/gateway/v1/run -H "Authorization: Bearer $OTTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"find_email","input":{"firstName":"Léa","lastName":"Marin","domain":"ateliers-marin.fr"}}'

# 4. Poll a queued or pending run
curl -s https://api.ezyagents.fr/gateway/v1/runs/<run_id> -H "Authorization: Bearer $OTTO_API_KEY"

# Balance and this key's monthly cap
curl -s https://api.ezyagents.fr/gateway/v1/balance -H "Authorization: Bearer $OTTO_API_KEY"
```

Run statuses: `queued`, `running`, `pending_approval`, `completed`,
`failed`, `rejected`, `cancelled`, `timed_out`. HTTP 200 = finished,
202 = queued or pending approval (poll `GET /runs/{run_id}`).

Files produced by a tool (Excel, PDF, images) come back as `result.files[]`
with an `fileId`; get a 5-minute link with
`POST https://api.ezyagents.fr/gateway/v1/files/{fileId}/download-url`.

## Errors (JSON: { "error": { "code", "message", ... } })

| HTTP | code | what to do |
|------|------|------------|
| 401 | invalid_key | ask the user for a valid key |
| 402 | credits_exhausted / ai_budget_exceeded | tell the user to top up in Settings → Billing; do not retry |
| 403 | scope_denied / policy_denied / tool_not_allowed | the key or the organisation forbids this tool; say so |
| 404 | unknown_tool / tool_not_exposed | run discover |
| 409 | connector_required | the user must connect a system: give them `settings_url` verbatim |
| 422 | invalid_input | read `errors`, fix the input, inspect again |
| 429 | rate_limited / key_cap_reached | slow down (120 req/min) or the key's monthly cap is reached |

## Rules

- Always inspect before running; never guess parameters.
- Start with small limits (5-10 items); report credits spent when the user
  cares about cost (`cost.credits` in every run response).
- `pending_approval` is normal, not an error: tell the user who was notified
  and wait or poll; never retry the same run, never work around it.
- Say clearly, before running, when a tool writes to the outside world
  (email, message, call, publication, CRM record, signature).
- A provider failure costs nothing. A rejected or expired approval costs nothing.

## Categories

- **Human resources** (16): list_employees, create_employee, create_candidate, list_candidates
- **Marketing** (15): draft_blog_post, draft_social_post, draft_newsletter, generate_email_campaign
- **Finance** (14): send_payment_reminder, parse_invoice, validate_vat_number, match_invoice_to_po
- **Customer support** (13): escalate_to_human, create_ticket, list_tickets, update_ticket_status
- **Routines & missions** (10): schedule_task, list_tasks, update_task, cancel_task
- **Prospecting** (9): search_prospects_apollo, search_prospects_linkedin, find_email, find_phone
- **Social & video** (9): publish_social_post, schedule_cross_post, plan_campaign, get_campaign_status
- **Knowledge** (8): search_knowledge, company_knowledge, remember, recall
- **Advertising** (7): generate_ad_visuals, generate_ad_video, check_ad_creatives, create_ad_campaign
- **CRM** (6): lookup_crm, create_contact_hubspot, salesforce_search, create_lead_salesforce
- **Email** (3): read_emails, send_email, reply_email
- **Slack & Teams** (2): post_slack_message, post_teams_message
- **Files & calendar** (2): drive_search, calendar_create_event
- **Images & voice** (2): generate_social_image, generate_image
- **Documents & code** (2): generate_document, run_code
- **Web** (2): web_search, web_fetch
- **Phone** (2): voice_call, voice_transcribe
- **French companies** (1): search_companies_pappers

Full catalogue with prices and schemas: https://ezyagents.fr/otto/outils
This file: https://ezyagents.fr/otto/SKILL.md
