Developers
Build on Ace.
One key opens every app your account connects: 1,600+ of them. Call them from your code, hand them to any AI chat over MCP, or ship them inside your own product.
Your keys
Keys act as your account. Keep them secret; revoke one any time.
Sign in to get a key.
Keys belong to your Ace account and reach the apps you connect on Integrations.
Sign in Create a free accountYour new key · shown once
Store it now. Ace keeps only a fingerprint and cannot show it again.
MCP: every app in every chat
Ace runs a remote MCP server. Any client that speaks MCP gets six tools: search_apps, connect_app, list_connections, find_tools, get_tool, run_tool.
Settings → Connectors → Add custom connector. Name it Ace and paste your link:
https://tryacee.com/mcp/k/ace_sk_YOUR_KEYThe link carries your key. Treat it like a password.
claude mcp add --transport http ace https://tryacee.com/mcp --header "Authorization: Bearer ace_sk_YOUR_KEY"Settings → Apps & Connectors → Advanced → turn on Developer mode, then Create. Paste your link and choose No authentication (the link holds your key):
https://tryacee.com/mcp/k/ace_sk_YOUR_KEYAdd to ~/.cursor/mcp.json:
{
"mcpServers": {
"ace": {
"url": "https://tryacee.com/mcp",
"headers": {
"Authorization": "Bearer ace_sk_YOUR_KEY"
}
}
}
}Add to .vscode/mcp.json:
{
"servers": {
"ace": {
"type": "http",
"url": "https://tryacee.com/mcp",
"headers": {
"Authorization": "Bearer ace_sk_YOUR_KEY"
}
}
}
}Streamable HTTP at https://tryacee.com/mcp with Authorization: Bearer <key>, or the URL-only form https://tryacee.com/mcp/k/<key>. Server card: /.well-known/mcp/server-card.json.
curl https://tryacee.com/mcp \
-H "Authorization: Bearer ace_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'SDKs
No dependencies. JavaScript runs in Node 18+, Deno, Bun, Workers and browsers. Python needs only the standard library.
npm install https://tryacee.com/sdk/tryacee-sdk-1.0.0.tgzimport { Ace } from "@tryacee/sdk";
const ace = new Ace({ apiKey: process.env.ACE_API_KEY });
// 1. Connect an app (send your user to the link once)
const { url } = await ace.apps.connect("slack");
// 2. Find the tool for a task, in plain words
const { tools } = await ace.tools.find("post a message in #sales on Slack");
// 3. Run it
const result = await ace.tools.run("SLACK_SEND_MESSAGE", {
channel: "#sales",
markdown_text: "Deal closed 🎉",
});pip install https://tryacee.com/sdk/tryacee-1.0.0-py3-none-any.whlfrom tryacee import Ace
ace = Ace() # reads ACE_API_KEY
link = ace.apps.connect("hubspot")["url"] # send the user here once
found = ace.tools.find("create a contact in HubSpot")
result = ace.tools.run("HUBSPOT_CREATE_CONTACT", {"email": "pat@example.com"})curl https://tryacee.com/api/v1/apps?search=slack \
-H "Authorization: Bearer ace_sk_YOUR_KEY"
curl -X POST https://tryacee.com/api/v1/tools/SLACK_SEND_MESSAGE/run \
-H "Authorization: Bearer ace_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"arguments":{"channel":"#sales","markdown_text":"Deal closed"}}'Hand every connected app to your own model. tools.definitions() returns OpenAI or Anthropic tool definitions; pass the model's tool call back to tools.run().
const tools = await ace.tools.definitions({ app: "gmail", format: "anthropic" });
const msg = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
messages: [{ role: "user", content: "Summarize my unread mail" }],
});
for (const block of msg.content)
if (block.type === "tool_use") await ace.tools.run(block.name, block.input);REST API
Base URL https://tryacee.com/api/v1 · Authorization: Bearer ace_sk_… · JSON in and out · errors are {"error":{"code","message"}} · 120 requests a minute per key · full spec at /openapi.json.
| Method | Path | What it does |
|---|---|---|
| GET | /catalog | Public app catalog, no key |
| GET | /apps?search= | Search apps with your connection status |
| GET | /apps/{app} | One app |
| POST | /apps/{app}/connect | Sign-in link that connects the app |
| POST | /apps/{app}/request | Any endpoint of a connected app's API: {method, path, query?, body?} |
| GET | /connections | Your connected apps |
| DELETE | /connections/{id} | Disconnect |
| GET | /tools?app= | Tools in an app, with input schemas |
| POST | /tools/find | Find tools for a task in plain words |
| GET | /tools/{tool} | One tool's schema |
| POST | /tools/{tool}/run | Run it in a connected app |
| GET | /custom-connectors | Your own connectors (any company's MCP server) |
| POST | /custom-connectors | Add an MCP server {name, url, key?} or any REST API {name, spec, base?, key?, keyIn?} |
| DELETE | /custom-connectors/{id} | Remove it |
| GET | /triggers/types?app= | Events an app can send (new payment, new issue, new message…) |
| POST | /triggers | Watch one: {type, config?, webhookUrl}, or take webhooks from any service: {source: "webhook", name, webhookUrl?} |
| GET | /triggers | Your triggers |
| DELETE | /triggers/{id} | Stop watching |
| GET | /events?trigger= | The last 50 events and how each delivery went |
| GET | /runs | Your 25 latest runs |
| GET | /me | The account behind the key |
Triggers: apps that call you
Pick an event in any connected app, give Ace your webhook, and every time it happens Ace posts it to you, signed. Ace keeps the last 50 events per trigger and tries each delivery three times.
const { triggers } = await ace.triggers.types({ app: "stripe" });
const { trigger, signingSecret } = await ace.triggers.create({
type: triggers[0].id,
webhookUrl: "https://your-app.com/hooks/ace",
});
// In your webhook handler: verify before trusting the body.
import { createHmac, timingSafeEqual } from "node:crypto";
const ts = req.headers["x-ace-timestamp"];
const expected = "sha256=" + createHmac("sha256", signingSecret).update(ts + "." + rawBody).digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-ace-signature"]));Recent plays
The last 25 actions run with your keys, MCP or this site.