Operations 5
Each operation below carries the questions people ask an LLM about it and the instructions they give an agent to run it. Generated by API Evangelist overlay
Each operation below carries the questions people ask an LLM about it and the instructions they give an agent to run it. Generated by API Evangelist overlay
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/ahasend:ahasend-routes-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: AhaSend API v2 Routes API
description: The AhaSend API v2 allows you to send transactional emails, manage domains, webhooks, routes, API keys, and view statistics.
version: 2.0.0
contact:
email: support@ahasend.com
license:
name: MIT
identifier: MIT
servers:
- url: https://api.ahasend.com
description: Production server
security:
- BearerAuth: []
tags:
- name: Routes
description: Manage inbound email routing
paths:
/v2/accounts/{account_id}/routes:
get:
summary: Get Routes
description: 'Returns a list of routes for the account. A domain-scoped key must
provide the matching `domain` query parameter; the global read role can
list without that filter.'
operationId: getRoutes
tags:
- Routes
parameters:
- name: account_id
in: path
required: true
description: Account ID
schema:
type: string
format: uuid
- name: domain
in: query
description: Domain to filter routes by. Required when using a domain-scoped routes:read:{domain} API key.
schema:
type: string
- name: limit
in: query
description: Maximum number of items to return (1-100)
schema:
type: integer
minimum: 1
maximum: 100
default: 100
- name: after
in: query
description: Pagination cursor for the next page. Provide the value provided in `next_cursor` from the response.
schema:
type: string
- name: before
in: query
description: Pagination cursor for the previous page.
schema:
type: string
security:
- BearerAuth:
- routes:read:all
- BearerAuth:
- routes:read:{domain}
x-code-samples:
- lang: go
label: AhaSend Go SDK
source: "package main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n\n \"github.com/AhaSend/ahasend-go/api\"\n \"github.com/google/uuid\"\n)\n\nfunc main() {\n // Create API client with authentication\n client := api.NewAPIClient(\n api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n )\n\n accountID := uuid.New()\n\n // Create context for the API call\n ctx := context.Background()\n\n // Call the ping endpoint\n response, httpResp, err := client.RoutesAPI.GetRoutes(\n ctx,\n accountID,\n nil,\n nil,\n )\n if err != nil {\n log.Fatalf(\"Error getting routes: %v\", err)\n }\n\n // Check response\n if httpResp.StatusCode == 200 {\n fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n if response != nil {\n fmt.Printf(\"Found %d routes\\n\", len(response.Data))\n }\n } else {\n fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n }\n}\n"
- lang: javascript
label: Node.js 22+ (AhaSend SDK)
source: 'import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
const page = await client.routes.list({ domain: "example.com", limit: 20 });
console.log("Routes listed.", { count: page.data.length });
'
responses:
'200':
description: List of routes
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedRoutesResponse'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create Route
description: 'Creates a new route for inbound email routing. Authorization uses the
domain in `recipient`: the global write role or its matching
domain-specific write role is sufficient.'
operationId: createRoute
tags:
- Routes
parameters:
- name: account_id
in: path
required: true
description: Account ID
schema:
type: string
format: uuid
- $ref: '#/components/parameters/IdempotencyKey'
security:
- BearerAuth:
- routes:write:all
- BearerAuth:
- routes:write:{domain}
x-code-samples:
- lang: go
label: AhaSend Go SDK
source: "package main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n\n \"github.com/AhaSend/ahasend-go/api\"\n \"github.com/AhaSend/ahasend-go/models/requests\"\n \"github.com/google/uuid\"\n)\n\nfunc main() {\n // Create API client with authentication\n client := api.NewAPIClient(\n api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n )\n\n accountID := uuid.New()\n\n // Create context for the API call\n ctx := context.Background()\n\n // Call the ping endpoint\n response, httpResp, err := client.RoutesAPI.CreateRoute(\n ctx,\n accountID,\n requests.CreateRouteRequest{\n Name: \"My Route\",\n URL: \"https://mystartup.com/tickets\",\n Recipient: \"ticket-*@mystartup.com\",\n Enabled: true,\n Attachments: true,\n Headers: true,\n StripReplies: true,\n },\n )\n if err != nil {\n log.Fatalf(\"Error creating route: %v\", err)\n }\n\n // Check response\n if httpResp.StatusCode == 201 {\n fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n if response != nil {\n fmt.Printf(\"Created route: %#v\\n\", response)\n }\n } else {\n fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n }\n}\n"
- lang: javascript
label: Node.js 22+ (AhaSend SDK)
source: "import { AhaSendClient } from \"@ahasend/sdk\";\n\nconst client = AhaSendClient.fromEnv();\nconst route = await client.routes.create(\n {\n name: \"Inbound messages\",\n url: \"https://example.com/inbound\",\n recipient: \"inbound@example.com\",\n },\n { idempotencyKey: \"sdk-sample-create-route\" },\n);\nconsole.log(\"Route created.\", { id: route.id, name: route.name });\n"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateRouteRequest'
responses:
'201':
description: Route created successfully
headers:
Idempotent-Replayed:
$ref: '#/components/headers/IdempotentReplayed'
content:
application/json:
schema:
$ref: '#/components/schemas/CreatedRoute'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'422':
$ref: '#/components/responses/IdempotencyPayloadMismatch'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/v2/accounts/{account_id}/routes/{route_id}:
get:
summary: Get Route
description: 'Returns a specific route by ID. Access requires the global read role or
a read role matching the route''s recipient domain.'
operationId: getRoute
tags:
- Routes
parameters:
- name: account_id
in: path
required: true
description: Account ID
schema:
type: string
format: uuid
- name: route_id
in: path
required: true
description: Route ID
schema:
type: string
format: uuid
security:
- BearerAuth:
- routes:read:all
- BearerAuth:
- routes:read:{domain}
x-code-samples:
- lang: go
label: AhaSend Go SDK
source: "package main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n\n \"github.com/AhaSend/ahasend-go/api\"\n \"github.com/google/uuid\"\n)\n\nfunc main() {\n // Create API client with authentication\n client := api.NewAPIClient(\n api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n )\n\n accountID := uuid.New()\n\n // Create context for the API call\n ctx := context.Background()\n\n // Call the ping endpoint\n response, httpResp, err := client.RoutesAPI.GetRoute(\n ctx,\n accountID,\n uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n )\n if err != nil {\n log.Fatalf(\"Error getting route: %v\", err)\n }\n\n // Check response\n if httpResp.StatusCode == 200 {\n fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n if response != nil {\n fmt.Printf(\"Route: %#v\\n\", response)\n }\n } else {\n fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n }\n}\n"
- lang: javascript
label: Node.js 22+ (AhaSend SDK)
source: 'import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
const routeId = "00000000-0000-4000-8000-000000000006";
const route = await client.routes.get(routeId);
console.log("Route found.", { id: route.id, name: route.name });
'
responses:
'200':
description: Route details
content:
application/json:
schema:
$ref: '#/components/schemas/Route'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Route not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
put:
summary: Update Route
description: 'Updates an existing route. A domain-scoped key must authorize the
existing recipient domain and, when `recipient` changes, the new domain;
`routes:write:all` satisfies both checks.'
operationId: updateRoute
tags:
- Routes
parameters:
- name: account_id
in: path
required: true
description: Account ID
schema:
type: string
format: uuid
- name: route_id
in: path
required: true
description: Route ID
schema:
type: string
format: uuid
security:
- BearerAuth:
- routes:write:all
- BearerAuth:
- routes:write:{domain}
x-code-samples:
- lang: go
label: AhaSend Go SDK
source: "package main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n\n \"github.com/AhaSend/ahasend-go\"\n \"github.com/AhaSend/ahasend-go/api\"\n \"github.com/AhaSend/ahasend-go/models/requests\"\n \"github.com/google/uuid\"\n)\n\nfunc main() {\n // Create API client with authentication\n client := api.NewAPIClient(\n api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n )\n\n accountID := uuid.New()\n\n // Create context for the API call\n ctx := context.Background()\n\n // Call the ping endpoint\n response, httpResp, err := client.RoutesAPI.UpdateRoute(\n ctx,\n accountID,\n uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n requests.UpdateRouteRequest{\n Name: ahasend.String(\"Updated Name\"),\n URL: ahasend.String(\"https://mystartup.com/new-tickets\"),\n },\n )\n if err != nil {\n log.Fatalf(\"Error updating route: %v\", err)\n }\n\n // Check response\n if httpResp.StatusCode == 200 {\n fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n if response != nil {\n fmt.Printf(\"Updated route: %#v\\n\", response)\n }\n } else {\n fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n }\n}\n"
- lang: javascript
label: Node.js 22+ (AhaSend SDK)
source: "import { AhaSendClient } from \"@ahasend/sdk\";\n\nconst client = AhaSendClient.fromEnv();\nconst routeId = \"00000000-0000-4000-8000-000000000006\";\nconst route = await client.routes.update(routeId, {\n url: \"https://example.com/inbound-v2\",\n});\nconsole.log(\"Route updated.\", { id: route.id, name: route.name });\n"
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateRouteRequest'
responses:
'200':
description: Route updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Route'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Route not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
delete:
summary: Delete Route
description: 'Deletes a route. Access requires the global delete role or a delete role
matching the route''s recipient domain.'
operationId: deleteRoute
tags:
- Routes
parameters:
- name: account_id
in: path
required: true
description: Account ID
schema:
type: string
format: uuid
- name: route_id
in: path
required: true
description: Route ID
schema:
type: string
format: uuid
security:
- BearerAuth:
- routes:delete:all
- BearerAuth:
- routes:delete:{domain}
x-code-samples:
- lang: go
label: AhaSend Go SDK
source: "package main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n\n \"github.com/AhaSend/ahasend-go/api\"\n \"github.com/google/uuid\"\n)\n\nfunc main() {\n // Create API client with authentication\n client := api.NewAPIClient(\n api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n )\n\n accountID := uuid.New()\n\n // Create context for the API call\n ctx := context.Background()\n\n // Call the ping endpoint\n response, httpResp, err := client.RoutesAPI.DeleteRoute(\n ctx,\n accountID,\n uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n )\n if err != nil {\n log.Fatalf(\"Error deleting route: %v\", err)\n }\n\n // Check response\n if httpResp.StatusCode == 200 {\n fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n if response != nil {\n fmt.Printf(\"Deleted route: %#v\\n\", response)\n }\n } else {\n fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n }\n}\n"
- lang: javascript
label: Node.js 22+ (AhaSend SDK)
source: 'import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
const routeId = "00000000-0000-4000-8000-000000000006";
const result = await client.routes.delete(routeId);
console.log("Route deleted.", { message: result.message });
'
responses:
'200':
description: Route deleted successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Route not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
CreateRouteRequest:
type: object
required:
- name
- url
- recipient
properties:
name:
type: string
maxLength: 255
description: Route name
url:
type: string
format: uri
description: Webhook URL for the route
recipient:
type: string
maxLength: 255
description: Recipient filter
attachments:
type: boolean
description: Whether to include attachments in webhooks
default: false
headers:
type: boolean
description: Whether to include headers in webhooks
default: false
group_by_message_id:
type: boolean
description: Whether to group by message ID
default: false
strip_replies:
type: boolean
description: Whether to strip reply content
default: false
enabled:
type: boolean
description: Whether the route is enabled
default: true
example:
name: Support Route
url: https://example.com/webhook
recipient: support@example.com
enabled: true
PaginatedRoutesResponse:
type: object
required:
- object
- data
- pagination
properties:
object:
type: string
enum:
- list
description: Object type identifier
data:
type: array
items:
$ref: '#/components/schemas/Route'
description: Array of routes
pagination:
$ref: '#/components/schemas/PaginationInfo'
UpdateRouteRequest:
type: object
properties:
name:
type:
- string
- 'null'
maxLength: 255
description: Route name
url:
type:
- string
- 'null'
format: uri
description: Webhook URL for the route
recipient:
type:
- string
- 'null'
maxLength: 255
description: Recipient filter
attachments:
type:
- boolean
- 'null'
description: Whether to include attachments in webhooks
headers:
type:
- boolean
- 'null'
description: Whether to include headers in webhooks
group_by_message_id:
type:
- boolean
- 'null'
description: Whether to group by message ID
strip_replies:
type:
- boolean
- 'null'
description: Whether to strip reply content
enabled:
type:
- boolean
- 'null'
description: Whether the route is enabled
example:
name: Updated Support Route
enabled: false
Route:
type: object
properties:
object:
type: string
enum:
- route
description: Object type identifier
id:
type: string
format: uuid
description: Unique identifier for the route
created_at:
type: string
format: date-time
description: When the route was created
updated_at:
type: string
format: date-time
description: When the route was last updated
name:
type: string
description: Route name
url:
type: string
format: uri
description: Webhook URL for the route
recipient:
type: string
description: Recipient filter; an empty string means no recipient filter
attachments:
type: boolean
description: Whether to include attachments in route payload
headers:
type: boolean
description: Whether to include headers in route payload
group_by_message_id:
type: boolean
description: Whether to group by message ID
strip_replies:
type: boolean
description: Whether to strip reply content
enabled:
type: boolean
description: Whether the route is enabled
success_count:
type: integer
minimum: 0
description: Number of successful calls
error_count:
type: integer
minimum: 0
description: Number of unsuccessful calls
errors_since_last_success:
type: integer
minimum: 0
description: 'Number of consecutive failed calls. Reset to zero by the next
successful call.
'
last_request_at:
type:
- string
- 'null'
format: date-time
description: When the route was last called
required:
- object
- id
- created_at
- updated_at
- name
- url
- recipient
- attachments
- headers
- group_by_message_id
- strip_replies
- enabled
- success_count
- error_count
- errors_since_last_success
- last_request_at
ErrorResponse:
type: object
description: 'Human-readable API error. The server currently sends no stable machine
error code. In particular, clients must not parse `message` to
distinguish IP-allow-list, scope, ownership, plan, self-lockout,
suppression-duplicate, or other errors that share an HTTP status.
'
additionalProperties: false
required:
- message
properties:
message:
type: string
description: Error description
example:
message: Error message
CreatedRoute:
allOf:
- $ref: '#/components/schemas/Route'
- type: object
properties:
secret:
type: string
description: Route signing secret. Returned only when the route is created.
required:
- secret
SuccessResponse:
type: object
required:
- message
properties:
message:
type: string
description: Success message
example:
message: Operation completed successfully
PaginationInfo:
type: object
required:
- has_more
properties:
has_more:
type: boolean
description: Whether more items exist after the last item of this page
next_cursor:
type: string
description: Pass as `after` to fetch the next page
previous_cursor:
type: string
description: Pass as `before` to fetch the previous page; absent on a `before` page when the start of the list has been reached
example:
has_more: true
next_cursor: eyJpZCI6MTIzNH0=
responses:
IdempotencyPayloadMismatch:
description: 'Idempotency key was already used with a different HTTP method, resolved
request path, or request body. This response includes neither
`Idempotent-Replayed` nor `Retry-After`.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
message: idempotency key was already used with a different request payload
IdempotencyConflict:
description: 'Request in progress. This response always includes
`Idempotent-Replayed: false` and a positive integer `Retry-After`.
Clients must use that header tuple, not the human-readable message, to
distinguish this retryable idempotency state from other HTTP 409s.
'
headers:
Idempotent-Replayed:
$ref: '#/components/headers/IdempotencyInProgress'
Retry-After:
$ref: '#/components/headers/IdempotencyRetryAfter'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
message: A request with this idempotency key is already in progress
headers:
IdempotentReplayed:
description: 'Indicates that this response is a stored replay of a previous identical request.
When this header is absent, the response is not a stored replay. Ordinary fresh
responses do not send `false`; that value is reserved for an in-progress 409.
'
schema:
type: string
enum:
- 'true'
IdempotencyInProgress:
description: 'Identifies an idempotency-key execution that is still in progress. This
header is emitted only on the specialized HTTP 409 response.
'
schema:
type: string
enum:
- 'false'
IdempotencyRetryAfter:
description: 'Positive whole number of seconds after which the in-progress execution
lease may be retried with the same key and unchanged request.
'
schema:
type: integer
minimum: 1
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
description: 'Optional idempotency key for safe request retries. Must be a unique string for each logical request.
An identical request with a completed stored outcome returns the original status and body. An in-progress
execution returns 409, a changed method/path/body returns 422, and a released 5xx execution may run again.
Keys for non-secret responses expire after 24 hours. API-key create responses include a one-time `secret_key`,
so successful encrypted replay responses for those operations expire after 5 minutes.
'
schema:
type: string
maxLength: 255
example: user-12345-create-domain-20240101
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: aha-sk-64-CHARACTER-RANDOM-STRING
description: 'API key for authentication. Non-empty Security Requirement values are
AhaSend API-key roles. Roles listed within one requirement object are
jointly required; separate requirement objects are alternatives.
'