Skip to main content

systemprompt_models/modules/
api_paths.rs

1//! API path constants.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_identifiers::{AgentName, ClientId, ContentHash, PluginId, TenantId};
7
8#[derive(Debug, Clone, Copy)]
9pub struct ApiPaths;
10
11impl ApiPaths {
12    pub const GATEWAY_BASE: &'static str = "/v1";
13    pub const GATEWAY_PUBLIC_BASE: &'static str = "/api/public/gateway";
14
15    pub const API_BASE: &'static str = "/api";
16    pub const API_V1: &'static str = "/api/v1";
17    pub const CORE_BASE: &'static str = "/api/v1/core";
18    pub const AGENTS_BASE: &'static str = "/api/v1/agents";
19    pub const MCP_BASE: &'static str = "/api/v1/mcp";
20    pub const STREAM_BASE: &'static str = "/api/v1/stream";
21    pub const CONTENT_BASE: &'static str = "/api/v1/content";
22    pub const SYNC_BASE: &'static str = "/api/v1/sync";
23    pub const ANALYTICS_BASE: &'static str = "/api/v1/analytics";
24    pub const META_BASE: &'static str = "/api/v1/meta";
25    pub const SLACK_BASE: &'static str = "/api/v1/slack";
26    pub const TEAMS_BASE: &'static str = "/api/v1/teams";
27
28    pub const CORE_CONTEXTS: &'static str = "/api/v1/core/contexts";
29    pub const CORE_TASKS: &'static str = "/api/v1/core/tasks";
30    pub const CORE_ARTIFACTS: &'static str = "/api/v1/core/artifacts";
31
32    pub const AGENT_RUNS: &'static str = "/api/v1/agent-runs";
33
34    pub const AGENTS_REGISTRY: &'static str = "/api/v1/agents/registry";
35
36    pub const MCP_REGISTRY: &'static str = "/api/v1/mcp/registry";
37
38    pub const STREAM_CONTEXTS: &'static str = "/api/v1/stream/contexts";
39
40    pub const AUTH_ME: &'static str = "/api/v1/auth/me";
41
42    pub const OAUTH_BASE: &'static str = "/api/v1/core/oauth";
43    pub const OAUTH_AUTHORIZE: &'static str = "/api/v1/core/oauth/authorize";
44    pub const OAUTH_TOKEN: &'static str = "/api/v1/core/oauth/token";
45    pub const OAUTH_CLIENTS: &'static str = "/api/v1/core/oauth/clients";
46
47    pub const CORE_USERS: &'static str = "/api/v1/core/users";
48
49    pub const WEBHOOK: &'static str = "/api/v1/webhook";
50
51    pub const HEALTH: &'static str = "/api/v1/health";
52
53    pub const LIVEZ: &'static str = "/livez";
54
55    pub const READYZ: &'static str = "/readyz";
56    pub const DISCOVERY: &'static str = "/api/v1";
57
58    pub const WELLKNOWN_BASE: &'static str = "/.well-known";
59    pub const WELLKNOWN_AGENT_CARD: &'static str = "/.well-known/agent-card.json";
60    pub const WELLKNOWN_AGENT_CARDS: &'static str = "/.well-known/agent-cards";
61    pub const WELLKNOWN_OAUTH_SERVER: &'static str = "/.well-known/oauth-authorization-server";
62    pub const WELLKNOWN_OPENID_CONFIG: &'static str = "/.well-known/openid-configuration";
63    pub const WELLKNOWN_OAUTH_PROTECTED: &'static str = "/.well-known/oauth-protected-resource";
64    pub const WELLKNOWN_JWKS: &'static str = "/.well-known/jwks.json";
65
66    pub const A2A_CARD: &'static str = "/api/a2a/card";
67
68    pub const ASSETS_BASE: &'static str = "/assets";
69    pub const FILES_BASE: &'static str = "/files";
70    pub const GENERATED_BASE: &'static str = "/generated";
71    pub const IMAGES_BASE: &'static str = "/images";
72    pub const STATIC_BASE: &'static str = "/static";
73    pub const NEXT_BASE: &'static str = "/_next";
74    pub const DOCS_BASE: &'static str = "/docs";
75    pub const SWAGGER_BASE: &'static str = "/swagger";
76    pub const OPENAPI_BASE: &'static str = "/openapi";
77
78    pub const TRACK_BASE: &'static str = "/track";
79    pub const TRACK_ENGAGEMENT: &'static str = "/track/engagement";
80
81    pub const ADMIN_BASE: &'static str = "/api/v1/admin";
82
83    pub const MARKETPLACE_BASE: &'static str = "/api/v1/marketplace";
84
85    pub const CLOUD_TENANTS: &'static str = "/api/v1/tenants";
86
87    pub const BRIDGE_BASE: &'static str = "/v1/bridge";
88    pub const BRIDGE_PUBKEY: &'static str = "/v1/bridge/pubkey";
89    pub const BRIDGE_MANIFEST: &'static str = "/v1/bridge/manifest";
90    pub const BRIDGE_PLUGINS: &'static str = "/v1/bridge/plugins";
91    pub const BRIDGE_PLUGIN_FILE_ROUTE: &'static str = "/v1/bridge/plugins/{plugin_id}/{*path}";
92    pub const BRIDGE_LATEST: &'static str = "/v1/bridge/latest";
93    pub const BRIDGE_DOWNLOAD: &'static str = "/v1/bridge/download";
94    pub const BRIDGE_DOWNLOAD_ROUTE: &'static str = "/v1/bridge/download/{platform}";
95    pub const BRIDGE_WHOAMI: &'static str = "/v1/bridge/whoami";
96    pub const BRIDGE_PROFILE: &'static str = "/v1/bridge/profile";
97    pub const BRIDGE_PROFILE_ENABLED_HOSTS: &'static str = "/v1/bridge/profile/enabled_hosts";
98    pub const BRIDGE_PROFILE_HOST_MODEL_FILTER: &'static str =
99        "/v1/bridge/profile/host-model-filter";
100    pub const BRIDGE_PROFILE_USAGE: &'static str = "/v1/bridge/profile/usage";
101    pub const BRIDGE_DEVICE: &'static str = "/v1/bridge/device";
102    pub const BRIDGE_HEARTBEAT: &'static str = "/v1/bridge/heartbeat";
103    pub const BRIDGE_STREAM: &'static str = "/v1/bridge/stream";
104    pub const BRIDGE_DRIVE_MANIFEST: &'static str = "/v1/bridge/drive/manifest";
105    pub const BRIDGE_DRIVE_CHANGES: &'static str = "/v1/bridge/drive/changes";
106    pub const BRIDGE_DRIVE_BLOB: &'static str = "/v1/bridge/drive/blob";
107    pub const BRIDGE_DRIVE_BLOB_ROUTE: &'static str = "/v1/bridge/drive/blob/{sha256}";
108    pub const BRIDGE_DRIVE_COMMIT: &'static str = "/v1/bridge/drive/commit";
109    pub const BRIDGE_DRIVE_PRESENCE: &'static str = "/v1/bridge/drive/presence";
110    pub const BRIDGE_DRIVE_PROPOSALS: &'static str = "/v1/bridge/drive/proposals";
111    pub const BRIDGE_DRIVE_TOKEN: &'static str = "/v1/bridge/drive/token";
112    pub const DOCUMENTS: &'static str = "/api/v1/documents";
113    pub const DOCUMENT_FOLDERS: &'static str = "/api/v1/document-folders";
114    pub const UPLOADS: &'static str = "/api/v1/uploads";
115    pub const BRIDGE_AUTH_PAT: &'static str = "/v1/auth/bridge/pat";
116    pub const BRIDGE_AUTH_SESSION: &'static str = "/v1/auth/bridge/session";
117    pub const BRIDGE_AUTH_SESSION_PAT: &'static str = "/v1/auth/bridge/session-pat";
118    pub const BRIDGE_AUTH_OAUTH_CLIENT: &'static str = "/v1/auth/bridge/oauth-client";
119    pub const BRIDGE_AUTH_CAPABILITIES: &'static str = "/v1/auth/bridge/capabilities";
120    pub const BRIDGE_PERSONAL_KEYS: &'static str = "/v1/me/provider-credentials";
121    pub const OTEL: &'static str = "/otel";
122    pub const OTEL_REST_ROUTE: &'static str = "/otel/{*rest}";
123    pub const MCP: &'static str = "/mcp";
124    pub const PUBLIC_HOOKS: &'static str = "/api/public/hooks";
125    pub const PUBLIC_HOOKS_TRACK: &'static str = "/api/public/hooks/track";
126    pub const CONSUMER_RECEIPTS: &'static str = "/api/v1/consumer/receipts";
127    pub const CONSUMER_SESSION_BINDINGS: &'static str = "/api/v1/consumer/session-bindings";
128    pub const CONSUMER_DEVICE_ENROLLMENT: &'static str = "/api/v1/consumer-devices/enrollment";
129    pub const CONSUMER_BUNDLE_ROUTE: &'static str =
130        "/api/v1/consumer/resources/{resource}/publications/{publication}/bundle";
131
132    // Why: `/otel` and `/mcp` are route families, not single endpoints; the
133    // bare path and every path below it belong to the family and nothing that
134    // merely shares the prefix (`/otelx`, `/mcpx`) does. Stated once here so
135    // the server's routing and the bridge's credential scoping cannot disagree.
136    #[must_use]
137    pub fn is_otel(path: &str) -> bool {
138        Self::in_family(path, Self::OTEL)
139    }
140
141    #[must_use]
142    pub fn is_mcp(path: &str) -> bool {
143        Self::in_family(path, Self::MCP)
144    }
145
146    #[must_use]
147    pub fn is_public_hook(path: &str) -> bool {
148        path.len() > Self::PUBLIC_HOOKS.len() + 1 && Self::in_family(path, Self::PUBLIC_HOOKS)
149    }
150
151    fn in_family(path: &str, base: &str) -> bool {
152        path == base
153            || path
154                .strip_prefix(base)
155                .is_some_and(|rest| rest.starts_with('/'))
156    }
157
158    #[must_use]
159    pub fn mcp_server_slug(path: &str) -> Option<&str> {
160        let rest = path.strip_prefix(Self::MCP)?.strip_prefix('/')?;
161        let slug = rest.split('/').next()?;
162        (!slug.is_empty()).then_some(slug)
163    }
164
165    #[must_use]
166    pub fn gateway_relative(path: &'static str) -> &'static str {
167        path.strip_prefix(Self::GATEWAY_BASE).unwrap_or(path)
168    }
169
170    #[must_use]
171    pub fn api_v1_relative(path: &'static str) -> &'static str {
172        path.strip_prefix(Self::API_V1).unwrap_or(path)
173    }
174
175    pub fn bridge_plugin_file(plugin_id: &PluginId, relative_path: &str) -> String {
176        format!(
177            "{}/{}/{relative_path}",
178            Self::BRIDGE_PLUGINS,
179            plugin_id.as_str()
180        )
181    }
182
183    pub fn bridge_drive_blob(sha256: &ContentHash) -> String {
184        format!("{}/{}", Self::BRIDGE_DRIVE_BLOB, sha256.as_str())
185    }
186
187    pub fn bridge_plugin_archive(plugin_id: &PluginId, sha256: &str) -> String {
188        format!(
189            "{}/{}/archive/{sha256}",
190            Self::BRIDGE_PLUGINS,
191            plugin_id.as_str()
192        )
193    }
194
195    pub fn bridge_download(platform: &str) -> String {
196        format!("{}/{platform}", Self::BRIDGE_DOWNLOAD)
197    }
198
199    pub fn otel_under(base_url: &str) -> String {
200        format!("{}{}", base_url.trim_end_matches('/'), Self::OTEL)
201    }
202
203    pub fn tenant(tenant_id: &TenantId) -> String {
204        format!("{}/{}", Self::CLOUD_TENANTS, tenant_id.as_str())
205    }
206
207    pub fn tenant_status(tenant_id: &TenantId) -> String {
208        format!("{}/{}/status", Self::CLOUD_TENANTS, tenant_id.as_str())
209    }
210
211    pub fn tenant_registry_token(tenant_id: &TenantId) -> String {
212        format!(
213            "{}/{}/registry-token",
214            Self::CLOUD_TENANTS,
215            tenant_id.as_str()
216        )
217    }
218
219    pub fn tenant_deploy(tenant_id: &TenantId) -> String {
220        format!("{}/{}/deploy", Self::CLOUD_TENANTS, tenant_id.as_str())
221    }
222
223    pub fn tenant_secrets(tenant_id: &TenantId) -> String {
224        format!("{}/{}/secrets", Self::CLOUD_TENANTS, tenant_id.as_str())
225    }
226
227    pub fn tenant_rotate_credentials(tenant_id: &TenantId) -> String {
228        format!(
229            "{}/{}/rotate-credentials",
230            Self::CLOUD_TENANTS,
231            tenant_id.as_str()
232        )
233    }
234
235    pub fn mcp_server_endpoint(server_name: &str) -> String {
236        format!("{}/{}/mcp", Self::MCP_BASE, server_name)
237    }
238
239    pub fn oauth_client_location(client_id: &ClientId) -> String {
240        format!("{}/{}", Self::OAUTH_CLIENTS, client_id.as_str())
241    }
242
243    pub fn wellknown_agent_card_named(agent_name: &str) -> String {
244        format!("{}/{}", Self::WELLKNOWN_AGENT_CARDS, agent_name)
245    }
246
247    pub fn agent_endpoint(agent_name: &AgentName) -> String {
248        format!("{}/{}/", Self::AGENTS_BASE, agent_name.as_str())
249    }
250}