{
  "openapi": "3.1.0",
  "info": {
    "title": "APIANT public APIs",
    "summary": "The public app catalog feed and the remote MCP server of APIANT.ai.",
    "description": "Two public surfaces of APIANT.ai, a hosted integration and automation platform operated by AI agents.\n\nThe app catalog feed at https://app.apiant.ai/catalog/v1 is public, read-only and needs no authentication. It lists every app you can connect, with the names and descriptions of its triggers and actions. Reference: https://apiant.ai/docs/reference/apps.\n\nThe MCP server at https://mcp.apiant.ai is a remote MCP server over Streamable HTTP. It needs an OAuth access token from https://app.apiant.ai (authorization code with PKCE). Reference: https://apiant.ai/docs/mcp/authentication.",
    "contact": {
      "name": "APIANT support",
      "url": "https://apiant.ai/support"
    },
    "termsOfService": "https://apiant.ai/tos",
    "version": "2026-10-26"
  },
  "externalDocs": {
    "description": "APIANT documentation",
    "url": "https://apiant.ai/docs"
  },
  "servers": [
    {
      "url": "https://app.apiant.ai/catalog/v1",
      "description": "Public app catalog feed. No authentication; sends Access-Control-Allow-Origin: *."
    }
  ],
  "tags": [
    {
      "name": "Catalog feed",
      "description": "Public, read-only app catalog.",
      "externalDocs": {
        "url": "https://apiant.ai/docs/reference/apps"
      }
    },
    {
      "name": "MCP server",
      "description": "Remote MCP server, OAuth required.",
      "externalDocs": {
        "url": "https://apiant.ai/docs/mcp/authentication"
      }
    }
  ],
  "paths": {
    "/apps": {
      "get": {
        "operationId": "getApps",
        "summary": "List every app in the catalog",
        "description": "Every app, summary fields only, in one response. The route takes no query parameters and does not paginate; count equals the length of apps. Also answers HEAD. Default rate limit: 300 per 5 minutes per IP, shared with /apps/{slug}. Rate limits are defaults and can change. IPv6 clients are counted per /64 network.",
        "tags": [
          "Catalog feed"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ifNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Cache-Control: public, max-age=300, stale-while-revalidate=3600.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppList"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/Catalog304"
          },
          "405": {
            "$ref": "#/components/responses/Catalog405"
          },
          "429": {
            "$ref": "#/components/responses/Catalog429"
          },
          "503": {
            "$ref": "#/components/responses/Catalog503"
          }
        }
      }
    },
    "/apps/{slug}": {
      "get": {
        "operationId": "getApp",
        "summary": "Get one app with its triggers and actions",
        "description": "One app with every summary field plus its triggers and actions. 404 for an unknown slug, a slug with uppercase or other invalid characters, or an app that is not in the feed. Also answers HEAD. Default rate limit: 300 per 5 minutes per IP, shared with /apps. Rate limits are defaults and can change. IPv6 clients are counted per /64 network.",
        "tags": [
          "Catalog feed"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/slug"
          },
          {
            "$ref": "#/components/parameters/ifNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Cache-Control: public, max-age=300, stale-while-revalidate=3600.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppDetail"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/Catalog304"
          },
          "404": {
            "$ref": "#/components/responses/Catalog404"
          },
          "405": {
            "$ref": "#/components/responses/Catalog405"
          },
          "429": {
            "$ref": "#/components/responses/Catalog429"
          },
          "503": {
            "$ref": "#/components/responses/Catalog503"
          }
        }
      }
    },
    "/apps/{slug}/logo": {
      "get": {
        "operationId": "getAppLogo",
        "summary": "Get an app's icon",
        "description": "The app icon bytes. 404 for an unknown slug or an app with no logo. Also answers HEAD. Default rate limit: 1,000 per 5 minutes per IP. Rate limits are defaults and can change. IPv6 clients are counted per /64 network.",
        "tags": [
          "Catalog feed"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/slug"
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Cache-Control: public, max-age=86400, immutable.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              }
            },
            "content": {
              "image/*": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "image/*",
                  "description": "The icon bytes."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Catalog404"
          },
          "405": {
            "$ref": "#/components/responses/Catalog405"
          },
          "429": {
            "$ref": "#/components/responses/Catalog429"
          }
        }
      }
    },
    "/bulk": {
      "get": {
        "operationId": "getBulk",
        "summary": "Download the whole catalog with triggers and actions",
        "description": "Every app with its triggers and actions, as a stored artifact rebuilt daily at 04:00 UTC; generated_at shows when. Sent gzip-compressed when the client sends Accept-Encoding: gzip. Prefer it over calling /apps/{slug} once per app. Also answers HEAD. Default rate limit: 10 per hour per IP. Rate limits are defaults and can change. IPv6 clients are counted per /64 network.",
        "tags": [
          "Catalog feed"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/ifNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "OK. Cache-Control: public, max-age=3600.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bulk"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/Catalog304"
          },
          "405": {
            "$ref": "#/components/responses/Catalog405"
          },
          "429": {
            "$ref": "#/components/responses/Catalog429"
          },
          "503": {
            "$ref": "#/components/responses/Catalog503"
          }
        }
      }
    },
    "/": {
      "servers": [
        {
          "url": "https://mcp.apiant.ai",
          "description": "APIANT remote MCP server, Streamable HTTP. OAuth access token required."
        }
      ],
      "post": {
        "operationId": "postMcpMessage",
        "summary": "Send an MCP message",
        "description": "Send one JSON-RPC message to the APIANT MCP server over the Streamable HTTP transport. Use an MCP client rather than calling this by hand. A request without a bearer token returns 401 with a WWW-Authenticate header naming the protected resource metadata.",
        "tags": [
          "MCP server"
        ],
        "security": [
          {
            "apiantOAuth": [
              "mcp:tools",
              "mcp:automation_builder"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcMessage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response, per the MCP Streamable HTTP transport.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcMessage"
                }
              }
            }
          },
          "401": {
            "description": "No bearer token, or an invalid one.",
            "headers": {
              "WWW-Authenticate": {
                "description": "Bearer resource_metadata=\"https://mcp.apiant.ai/.well-known/oauth-protected-resource\"",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Operation": {
        "type": "object",
        "description": "A trigger or action. The feed publishes names and descriptions only: no endpoints, request bodies, credentials or internal identifiers.",
        "required": [
          "name",
          "description"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the trigger or action."
          },
          "description": {
            "type": "string",
            "description": "What the trigger or action does."
          }
        }
      },
      "AppSummary": {
        "type": "object",
        "description": "An app entry with its summary fields, as listed by GET /apps.",
        "required": [
          "slug",
          "name",
          "description",
          "category",
          "auth_type",
          "app_domain",
          "logo_url",
          "connect_url",
          "last_updated",
          "etag",
          "retired",
          "tier",
          "sort_order",
          "trigger_count",
          "action_count",
          "last_op_modified"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "The app's public identifier, lowercase letters, digits and hyphens. The only handle the feed exposes.",
            "pattern": "^[a-z0-9-]+$"
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "One-paragraph description of the app."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "App category. null when no category has been set yet."
          },
          "auth_type": {
            "type": "string",
            "description": "How the app connects. Values seen on the docs page (2026-09-15): api_key, none, oauth_v2, managed_auth, token_mint. Also seen in live responses (2026-10-26): hmac_sign, jwt_sign. Treat it as an open set."
          },
          "app_domain": {
            "type": "string",
            "description": "The vendor's domain, for example slack.com."
          },
          "logo_url": {
            "type": "string",
            "description": "URL of the app icon (/catalog/v1/apps/{slug}/logo).",
            "format": "uri"
          },
          "connect_url": {
            "type": "string",
            "description": "Sign-up link that starts on this app: https://app.apiant.ai/signup?app={slug}.",
            "format": "uri"
          },
          "last_updated": {
            "type": "string",
            "description": "RFC 3339 time the connector or one of its listed operations was last edited. Informational; use etag to detect change.",
            "format": "date-time"
          },
          "etag": {
            "type": "string",
            "description": "Validator for this app's entry, including its operations."
          },
          "retired": {
            "type": "boolean",
            "description": "true when the app has been withdrawn. A retired app stays in the feed with this marker for a retirement window (180 days by default), then drops out."
          },
          "retired_at": {
            "type": "string",
            "description": "Present only when retired is true.",
            "format": "date-time"
          },
          "tier": {
            "type": "string",
            "description": "Popularity band: A, B, C, or D for unranked.",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ]
          },
          "sort_order": {
            "type": "integer",
            "description": "Position 1 to N in the feed's popularity ordering. It shifts when apps enter or leave the feed, so treat it as a sort key, not an identifier."
          },
          "trigger_count": {
            "type": "integer",
            "description": "Number of triggers."
          },
          "action_count": {
            "type": "integer",
            "description": "Number of actions."
          },
          "last_op_modified": {
            "type": [
              "string",
              "null"
            ],
            "description": "When a trigger or action on this app was last added or edited. null when the app has none.",
            "format": "date-time"
          },
          "published_at": {
            "type": "string",
            "description": "RFC 3339 time. Present in live responses (2026-10-26) but not described on the catalog reference page.",
            "format": "date-time"
          }
        }
      },
      "AppDetail": {
        "type": "object",
        "description": "An app entry with every summary field plus its triggers and actions, as returned by GET /apps/{slug} and inside GET /bulk.",
        "required": [
          "slug",
          "name",
          "description",
          "category",
          "auth_type",
          "app_domain",
          "logo_url",
          "connect_url",
          "last_updated",
          "etag",
          "retired",
          "tier",
          "sort_order",
          "trigger_count",
          "action_count",
          "last_op_modified",
          "triggers",
          "actions"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "The app's public identifier, lowercase letters, digits and hyphens. The only handle the feed exposes.",
            "pattern": "^[a-z0-9-]+$"
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "One-paragraph description of the app."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "App category. null when no category has been set yet."
          },
          "auth_type": {
            "type": "string",
            "description": "How the app connects. Values seen on the docs page (2026-09-15): api_key, none, oauth_v2, managed_auth, token_mint. Also seen in live responses (2026-10-26): hmac_sign, jwt_sign. Treat it as an open set."
          },
          "app_domain": {
            "type": "string",
            "description": "The vendor's domain, for example slack.com."
          },
          "logo_url": {
            "type": "string",
            "description": "URL of the app icon (/catalog/v1/apps/{slug}/logo).",
            "format": "uri"
          },
          "connect_url": {
            "type": "string",
            "description": "Sign-up link that starts on this app: https://app.apiant.ai/signup?app={slug}.",
            "format": "uri"
          },
          "last_updated": {
            "type": "string",
            "description": "RFC 3339 time the connector or one of its listed operations was last edited. Informational; use etag to detect change.",
            "format": "date-time"
          },
          "etag": {
            "type": "string",
            "description": "Validator for this app's entry, including its operations."
          },
          "retired": {
            "type": "boolean",
            "description": "true when the app has been withdrawn. A retired app stays in the feed with this marker for a retirement window (180 days by default), then drops out."
          },
          "retired_at": {
            "type": "string",
            "description": "Present only when retired is true.",
            "format": "date-time"
          },
          "tier": {
            "type": "string",
            "description": "Popularity band: A, B, C, or D for unranked.",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ]
          },
          "sort_order": {
            "type": "integer",
            "description": "Position 1 to N in the feed's popularity ordering. It shifts when apps enter or leave the feed, so treat it as a sort key, not an identifier."
          },
          "trigger_count": {
            "type": "integer",
            "description": "Number of triggers."
          },
          "action_count": {
            "type": "integer",
            "description": "Number of actions."
          },
          "last_op_modified": {
            "type": [
              "string",
              "null"
            ],
            "description": "When a trigger or action on this app was last added or edited. null when the app has none.",
            "format": "date-time"
          },
          "published_at": {
            "type": "string",
            "description": "RFC 3339 time. Present in live responses (2026-10-26) but not described on the catalog reference page.",
            "format": "date-time"
          },
          "triggers": {
            "type": "array",
            "description": "Detail and bulk responses only. The app's triggers.",
            "items": {
              "$ref": "#/components/schemas/Operation"
            }
          },
          "actions": {
            "type": "array",
            "description": "Detail and bulk responses only. The app's actions.",
            "items": {
              "$ref": "#/components/schemas/Operation"
            }
          }
        }
      },
      "AppList": {
        "type": "object",
        "description": "Every app in the feed, summary fields only.",
        "required": [
          "generated_at",
          "count",
          "apps"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this response was generated."
          },
          "count": {
            "type": "integer",
            "description": "Number of apps; equals the length of apps."
          },
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppSummary"
            }
          }
        }
      },
      "Bulk": {
        "type": "object",
        "description": "Every app with its triggers and actions, built once a day.",
        "required": [
          "generated_at",
          "count",
          "apps"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the daily artifact was built."
          },
          "count": {
            "type": "integer",
            "description": "Number of apps; equals the length of apps."
          },
          "apps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppDetail"
            }
          }
        }
      },
      "FeedError": {
        "type": "object",
        "description": "The catalog feed's JSON error body (503).",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong."
          }
        },
        "examples": [
          {
            "error": "catalog feed temporarily unavailable"
          }
        ]
      },
      "McpError": {
        "type": "object",
        "description": "The MCP server's JSON error body.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code, for example unauthorized."
              },
              "message": {
                "type": "string",
                "description": "What went wrong."
              },
              "hint": {
                "type": "string",
                "description": "How to resolve it."
              }
            }
          }
        },
        "examples": [
          {
            "error": {
              "code": "unauthorized",
              "message": "This request has no bearer token.",
              "hint": "Connect to https://mcp.apiant.ai with an MCP client and sign in with OAuth."
            }
          }
        ]
      },
      "JsonRpcMessage": {
        "type": "object",
        "description": "A JSON-RPC 2.0 message as defined by the Model Context Protocol.",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          }
        }
      }
    },
    "parameters": {
      "slug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "description": "The app's slug, for example slack.",
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9-]+$"
        }
      },
      "ifNoneMatch": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "description": "An ETag from an earlier response. The feed answers 304 with no body when nothing changed.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "ETag": {
        "description": "Validator for this response. Send it back in If-None-Match.",
        "schema": {
          "type": "string"
        }
      },
      "Cache-Control": {
        "description": "Caching policy of the route.",
        "schema": {
          "type": "string"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "Allow": {
        "description": "Methods the route accepts: GET, HEAD.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Catalog304": {
        "description": "If-None-Match matched the current ETag. No body.",
        "headers": {
          "ETag": {
            "$ref": "#/components/headers/ETag"
          }
        }
      },
      "Catalog404": {
        "description": "Unknown slug, a slug with uppercase or other invalid characters, an app that is not in the feed, or an app with no logo. No body."
      },
      "Catalog405": {
        "description": "A method other than GET or HEAD. Body: method not allowed.",
        "headers": {
          "Allow": {
            "$ref": "#/components/headers/Allow"
          }
        },
        "content": {
          "text/plain": {
            "schema": {
              "type": "string",
              "const": "method not allowed"
            }
          }
        }
      },
      "Catalog429": {
        "description": "The rate limit for that route was passed. Body: rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "text/plain": {
            "schema": {
              "type": "string",
              "const": "rate limit exceeded"
            }
          }
        }
      },
      "Catalog503": {
        "description": "The feed cannot produce a body it trusts, or the daily bulk artifact is not built yet or lists an app the feed no longer serves. The feed never returns 200 with an empty app list.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FeedError"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "apiantOAuth": {
        "type": "oauth2",
        "description": "OAuth 2 authorization code with PKCE (S256) against https://app.apiant.ai. Dynamic client registration at https://app.apiant.ai/oauth/client. Metadata: https://mcp.apiant.ai/.well-known/oauth-protected-resource and https://app.apiant.ai/.well-known/oauth-authorization-server. A sign-in that asks for no scope is granted both. Scopes do not narrow which tools a signed-in user can call; permissions and plan do.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.apiant.ai/oauth/login",
            "tokenUrl": "https://app.apiant.ai/oauth/token",
            "refreshUrl": "https://app.apiant.ai/oauth/token",
            "scopes": {
              "mcp:tools": "Use the APIANT tools in your account: read and change your automations, assemblies and connections, and run them. Required for every tool call.",
              "mcp:automation_builder": "Build and edit automations on your behalf."
            }
          }
        }
      }
    }
  }
}
