IF IT SHOULDN'T HAPPEN BY ACCIDENT, PUT CY ON IT.
Fine on purpose, terrible by mistake. That's the whole list.
GATE A DEPLOY ON A KEY TURN.
Add Outis as a protection rule on your production environment. Any job that targets it waits until the keys turn. Nothing to store in the repo.
GOOD FOR
Production deploys2 KEYS
Merging to main1 KEY
PR reviews1 KEY
on:
push:
tags: ["v*"]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # waits for the keys
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
ASK FROM THE CHANNEL YOU'RE ALREADY IN.
Type /outis mid-incident. The request goes to whoever policy names, and their codes arrive as DMs.
GOOD FOR
Break-glass access2 KEYS
Hotfixes2 KEYS
Admin grants2 KEYS
dana /outis db.break-glass cluster=payments-prod ttl=15m
OUTIS Requested db.break-glass on payments-prod.
Waiting on 2 keys: keith, maya.
Codes sent by DM.
OUTIS Authorized. keith, maya · 14:05
Credential expires 14:20.
PUT A KEY ON ANY FUNCTION.
The SDK asks, waits, and hands back the answer. Your params show on the approver's screen, and policy stays in Outis.
GOOD FOR
Large payouts3 KEYS
Key rotation2 KEYS
Bulk data export2 KEYS
Destroying things3 KEYS
import { Outis } from "@outis/sdk";
const outis = new Outis({ apiKey: process.env.OUTIS_KEY });
const res = await outis.request({
action: "payouts.release",
params: { amount: "$48,000", to: "acct_9f2" },
}).wait();
if (res.authorized) await releasePayout();
client := outis.New(os.Getenv("OUTIS_KEY"))
res, err := client.Request(ctx, outis.Req{
Action: "payouts.release",
Params: map[string]string{
"amount": "$48,000",
"to": "acct_9f2",
},
}).Wait(ctx)
if err == nil && res.Authorized {
releasePayout()
}
from outis import Outis
outis = Outis(api_key=os.environ["OUTIS_KEY"])
res = outis.request(
action="payouts.release",
params={"amount": "$48,000", "to": "acct_9f2"},
).wait()
if res.authorized:
release_payout()
curl -X POST https://app.outis.dev/v1/requests \
-H "Authorization: Bearer $OUTIS_KEY" \
-d '{"action":"payouts.release",
"params":{"amount":"$48,000","to":"acct_9f2"}}'
# then poll GET /v1/requests/{id}
# or pass a callback_url and we'll call you
LET THE AGENT ASK. A PERSON TURNS THE KEY.
Claude, ChatGPT or your own agent calls Outis before anything it can't undo, then waits. A tap in chat is easy to rubber-stamp. A key and a code aren't.
GOOD FOR
Agent-run deploys2 KEYS
Deleting data2 KEYS
Spending money2 KEYS
Posting in public1 KEY
{
"mcpServers": {
"outis": {
"command": "npx",
"args": ["-y", "@outis/mcp"],
"env": { "OUTIS_KEY": "ok_live_..." }
}
}
}
# Claude gets an outis.request tool.
# It asks before anything risky, then waits for the key.
Name Outis
URL https://mcp.outis.dev
Auth Sign in with Outis
# ChatGPT can now call outis.request.
# The chat pauses until a person turns a key.
import { guard } from "@outis/sdk";
const dropTable = guard(db.dropTable, {
action: "db.drop-table",
summary: (t) => "Drop " + t,
});
// the agent calls it like any tool.
// it runs only after a key turns.
await dropTable("users_archive");
PLUGS INTO YOUR STACK.
Requests come in from the tools you already use. Codes go out where your people already are.
REQUESTS FROM
GitHub Actions
AVAILABLE
Slack /outis
AVAILABLE
HTTP API
AVAILABLE
TF
Terraform
SOON
K8
Kubernetes
SOON
AW
AWS
SOON
ST
Stripe
SOON
OK
Okta
SOON
PD
PagerDuty
SOON
CL
Claude
SOON
GP
ChatGPT
SOON
AI
Any MCP agent
SOON
CODES SENT TO
Not on the list? SDKs for TypeScript, Go and Python, or plain HTTP.
THE INTEGRATION GUIDE →
Slack
AVAILABLE
Discord
SOON
SMS
SOON
Email
SOON