Install
$ agentstack add skill-maton-ai-api-gateway-skill-api-gateway-skill Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Reads credentials/environment and may exfiltrate them.
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
API Gateway
Managed API routing for third-party services, provided by Maton. Use this only for a user-requested app, account, and task.
Quick Start
CLI:
maton slack channel list --types public_channel --limit 10
maton api '/slack/api/conversations.list?types=public_channel&limit=10'
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/slack/api/conversations.list?types=public_channel&limit=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Routing
Use https://api.maton.ai/ with the app-prefixed routes documented in the examples below or in the matching reference file.
Usage protocol:
- Only invoke after the user specifies the exact app, account, and task.
- Always start with read-only (GET) calls to verify the target account, resource identifiers, and current state.
- All non-GET requests are denied unless the user explicitly approves each one. Before any POST, PUT, PATCH, or DELETE call, present the user with: the exact connection ID, the full endpoint path, the request body, and the expected outcome — then wait for approval.
- If the user's request implies a non-GET operation, first show them what you intend to call and ask for confirmation. Do not infer approval from the original request.
Read-only route examples:
https://api.maton.ai/slack/api/conversations.list?types=public_channel&limit=10
https://api.maton.ai/google-mail/gmail/v1/users/me/messages
The first path segment is the app identifier listed in Supported Services. For Gmail, use /google-mail/gmail/v1/users/me/messages.
Installation
NPM:
npm install -g @maton/cli
Homebrew:
brew install maton-ai/cli/maton
Authentication
IMPORTANT — Credential Safety:
- Treat
MATON_API_KEYas a secret. Never log it, echo it, paste it into prompts, or expose it in shared files, command output, or tool results. - Connection creation requires explicit user approval. Before creating any connection, ask the user to confirm the specific service and confirm they intend to authorize access. Never create connections on the agent's own initiative.
- Least-privilege scopes: When a service offers scope selection during OAuth, select only the scopes the current task requires. Do not accept broader scopes for convenience.
- Remove connections immediately after the task is complete if they are no longer needed (
maton connection delete {id}). - If the key may have been exposed (logs, screenshots, shared terminals), rotate it immediately at maton.ai/settings.
- Never share the key across users, workflows, or environments that do not require it.
CLI:
maton login # Opens browser for API key
maton login --interactive # Skip browser, paste API key directly
maton whoami # Show current auth state
Manual:
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Click the copy button on the right side of API Key section to copy it
- Set your API key as
MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Connection Management
List Connections
CLI:
maton connection list slack --status ACTIVE
maton api -X GET /connections -f app=slack -f status=ACTIVE
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=slack&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Query Parameters (optional):
app- Filter by service name (e.g.,slack,hubspot,salesforce)status- Filter by connection status (ACTIVE,PENDING,FAILED)
Response:
{
"connections": [
{
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=5e9...",
"app": "slack",
"method": "OAUTH2",
"metadata": {}
}
]
}
Create Connection
CLI:
maton connection create slack
maton api /connections -f app=slack
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'slack'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Request Body:
app(required) - Service name (e.g.,slack,notion)method(optional) - Connection method (API_KEY,BASIC,OAUTH1,OAUTH2,MCP)
Get Connection
CLI:
maton connection get {connection_id}
maton api /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=5e9...",
"app": "slack",
"metadata": {}
}
}
Open the returned URL in a browser to complete service authorization.
Delete Connection
CLI:
maton connection delete {connection_id} --yes
maton api -X DELETE /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple connections for the same app, specify which connection to use:
CLI:
maton slack channel list --types public_channel --limit 10 --connection {connection_id}
maton api '/slack/api/conversations.list?types=public_channel&limit=10' --connection {connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/slack/api/conversations.list?types=public_channel&limit=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always specify the connection to ensure requests go to the intended account.
Trigger Management
List Triggers
CLI:
maton trigger list --source github --status ENABLED -L 50
maton api -X GET /triggers -f source=github -f status=ENABLED -f limit=50
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/triggers?source=github&status=ENABLED&limit=50')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Query Parameters (optional): source, status, limit, next_token.
Response:
{
"triggers": [
{
"trigger_id": "{trigger_id}",
"source": "github",
"event_type": "pull_request.opened",
"name": "PR opened",
"description": null,
"parameters": {"repo": "maton-ai/cli"},
"connection_id": "{connection_id}",
"destinations": [
{
"destination_id": "{destination_id}",
"url": "https://httpbin.org/post",
"name": null,
"status": "ENABLED",
"reason": null
}
],
"status": "ENABLED",
"reason": null,
"created_at": "2026-05-25T23:24:38.079501Z",
"updated_at": "2026-05-25T23:24:38.079501Z"
}
],
"next_token": "gAAAAABqN6tD5X7..."
}
Create Trigger
CLI:
maton trigger create --source github --event-type pull_request.opened \
--connection-id {connection_id} \
--parameter repo=maton-ai/cli \
--destination '{"url":"https://httpbin.org/post","method":"POST","name":"prod"}'
maton api /triggers \
-f source=github -f event_type=pull_request.opened \
-f name='PR opened' -f connection_id={connection_id} \
-F 'parameters[repo]=maton-ai/cli' \
-F 'destinations[][url]=https://httpbin.org/post' \
-F 'destinations[][method]=POST' \
-F 'destinations[][name]=prod'
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
"source": "github",
"event_type": "pull_request.opened",
"name": "PR opened",
"connection_id": "{connection_id}",
"parameters": {"repo": "maton-ai/cli"},
"destinations": [{"url": "https://httpbin.org/post", "method": "POST", "name": "prod"}]
}).encode()
req = urllib.request.Request('https://api.maton.ai/triggers', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Request Body:
source(required)event_type(required)connection_id(optional)name,description(optional)parameters(optional)destinations(optional)
Each source's event types and their parameters are documented at references/{source}/triggers.md (e.g. google-mail). Besides the app sources in the Supported Services table, the special time source fires on a cron schedule (schedule.elapsed) and needs no connection.
Get Trigger
CLI:
maton trigger get {trigger_id}
maton api /triggers/{trigger_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/triggers/{trigger_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"trigger": {
"trigger_id": "{trigger_id}",
"source": "stripe",
"event_type": "charge.succeeded",
"name": "Charges",
"description": null,
"parameters": {"event_type": "charge.succeeded"},
"connection_id": "{connection_id}",
"destinations": [
{
"destination_id": "{destination_id}",
"url": "https://httpbin.org/post",
"name": null,
"status": "ENABLED",
"reason": null
}
],
"status": "ENABLED",
"reason": null,
"created_at": "2026-05-25T23:27:50.166333Z",
"updated_at": "2026-05-25T23:27:50.166333Z"
}
}
Update Trigger
Edits trigger metadata only. Destinations are managed through their own endpoints.
CLI:
maton trigger update {trigger_id} --parameter repo=maton-ai/cli
maton api -X PATCH /triggers/{trigger_id} -F 'parameters[repo]=maton-ai/cli'
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({"parameters": {"repo": "maton-ai/cli"}}).encode()
req = urllib.request.Request('https://api.maton.ai/triggers/{trigger_id}', data=data, method='PATCH')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Request Body: name, description, status, parameters (replaces all).
Delete Trigger
CLI:
maton trigger delete {trigger_id} --yes
maton api -X DELETE /triggers/{trigger_id}
Python:
python <<'EOF'
import urllib.request, os
req = urllib.request.Request('https://api.maton.ai/triggers/{trigger_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
urllib.request.urlopen(req)
EOF
List Destinations
CLI:
maton trigger destination list --trigger {trigger_id}
maton api -X GET /triggers/{trigger_id}/destinations
Response:
{
"destinations": [
{
"destination_id": "{destination_id}",
"url": "https://httpbin.org/post",
"name": null,
"status": "ENABLED",
"reason": null
}
]
}
Create Destination
CLI:
maton trigger destination create --trigger {trigger_id} \
--url https://httpbin.org/post --method POST --name prod \
--header X-Token=secret \
--body-template '{"data": {{ payload.data }}}'
maton api /triggers/{trigger_id}/destinations \
-f url=https://httpbin.org/post -f method=POST -f name=prod \
-F 'headers[X-Token]=secret' \
-f 'body_template={"data": {{ payload.data }}}'
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
"url": "https://httpbin.org/post",
"method": "POST",
"name": "prod",
"headers": {"X-Token": "secret"},
"body_template": '{"data": {{ payload.data }}}'
}).encode()
req = urllib.request.Request('https://api.maton.ai/triggers/{trigger_id}/destinations', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Request Body:
url(required)method(optional, default:POST)name(optional)headers(optional)body_template(optional) — JSON template for the outgoing request body, with{{ ... }}placeholders interpolated at delivery time. Seereferences/{source}/triggers.mdfor each source's payload shape and available fields.
Template placeholders:
{{ payload }}— the full event payload, inlined as JSON{{ payload.x.y.z }}— drill into a nested field inside the payload{{ trigger_id }},{{ trigger_name }},{{ event_id }},{{ source }},{{ event_type }}— scalar metadata{{ received_at }}— when the event was received
Get Destination
CLI:
maton trigger destination get {destination_id} --trigger {trigger_id}
maton api -X GET /triggers/{trigger_id}/destinations/{destination_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/triggers/{trigger_id}/destinations/{destination_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"destination": {
"destination_id": "{destination_id}",
"url": "https://httpbin.org/post",
"method": "POST",
"headers": {},
"signing_secret": "••••••••",
"name": null,
"body_template": null,
"status": "ENABLED",
"reason": null,
"created_at": "2026-05-25T23:27:50.166333Z",
"updated_at": "2026-05-25T23:27:50.166333Z"
}
}
signing_secret is masked; retrieve the plaintext value only at create time or via Rotate Destination Secret.
Update Destination
CLI:
maton trigger destination update {destination_id} --trigger {trigger_id} --url https://new.dev/hook
ma
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [maton-ai](https://github.com/maton-ai)
- **Source:** [maton-ai/api-gateway-skill](https://github.com/maton-ai/api-gateway-skill)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.