A small, Bearer-authenticated JSON API for SaaSPilot: list your pilots and actions, run actions and read their run history.
Pointing an AI agent at this API?
Hand it the LLM-ready Markdown version — self-contained instructions an agent can follow with just a URL and an API key.
All endpoints live under `https://ypk0ho4z.vibecode.cloud/api/v1`. `https://ypk0ho4z.vibecode.cloud` is the origin you were given (scheme + host, e.g. `https://example.com`). Do not add a trailing slash.
Every endpoint requires an API key sent as a Bearer token: `Authorization: Bearer sk_your_key_here`. Keys always start with `sk_`. A missing or invalid key returns `401 { "error": "Invalid or missing API key" }`. Create keys in the app under Profile → API Keys.
Responses are JSON unless noted (file download returns raw bytes). Request bodies are JSON (`Content-Type: application/json`) except file upload, which is `multipart/form-data`.
Requests are rate limited per API key. When you exceed a limit you get `429` (or `403` if the limit is configured to block) with an `error` message and, when applicable, a `Retry-After` header (seconds). Back off and retry.
Actions you publish get their own public URL `https://ypk0ho4z.vibecode.cloud/api/p/<slug>` (GET with query params as input, or POST a JSON object). Those are NOT part of this API and do not use `sk_` keys: depending on the endpoint they are open, password-protected (HTTP Basic or `?key=`) or use a per-endpoint key (`X-API-Key: spk_...`). Their output format (json/csv/html/xml/md/text) is chosen per endpoint and can be overridden with `?format=`.
Errors are JSON with an `error` string and a matching HTTP status (`400` bad input, `401` unauthenticated, `403` forbidden, `404` not found, `413` payload too large, `429` rate limited, `500` server error).
Your base URL is https://ypk0ho4z.vibecode.cloud. Create API keys under Profile → API Keys.
/api/v1/healthConfirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/health \
-H "Authorization: Bearer sk_your_key_here"Response
{
"status": "healthy",
"timestamp": "2026-07-19T12:00:00.000Z",
"uptime": 1234.56,
"version": "1.0.0",
"apiKey": "My key",
"userId": "usr_...",
"message": "API is running successfully"
}/api/v1/statsReturns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/stats \
-H "Authorization: Bearer sk_your_key_here"Response
{
"user": { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "createdAt": "..." },
"apiStats": {
"totalApiKeys": 2,
"requestsToday": 14,
"requestsThisWeek": 98,
"requestsThisMonth": 412,
"errorRate": "1.20%",
"errorCount": 5
},
"meta": { "timestamp": "...", "apiKey": "My key" }
}/api/v1/usersLists users. A regular key returns only its own user record; an admin key returns all users with pagination.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| limit | query | integer | no | Page size, 1–100 (default 10). Admin only; ignored for non-admins. |
| offset | query | integer | no | Rows to skip (default 0). Admin only. |
Request
curl "https://ypk0ho4z.vibecode.cloud/api/v1/users?limit=20&offset=0" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"users": [
{ "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "emailVerified": null, "createdAt": "..." }
],
"meta": { "limit": 20, "offset": 0, "total": 1, "apiKey": "My key" }
}/api/v1/usersAdmin-only endpoint scaffold for creating a user. Ships as a stub in this starter — it validates input and echoes it back rather than persisting. Fill in real creation logic before relying on it.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| body | string | yes | New user email. | |
| name | body | string | yes | New user display name. |
| role | body | string | no | 'user' (default) or 'admin'. |
Request
curl -X POST https://ypk0ho4z.vibecode.cloud/api/v1/users \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"email":"new@example.com","name":"New User","role":"user"}'Response
{
"message": "User creation endpoint - implementation needed",
"requestedData": { "email": "new@example.com", "name": "New User", "role": "user" },
"apiKey": "My key"
}/api/v1/filesUploads a file and stores its raw bytes. Use this instead of a form/Server Action for any real upload (Server Actions cap the body at ~1MB; this endpoint does not). Send `multipart/form-data` with a single `file` field.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| file | form | file | yes | The file to upload (multipart field name must be "file"). |
Request
curl -X POST https://ypk0ho4z.vibecode.cloud/api/v1/files \
-H "Authorization: Bearer sk_your_key_here" \
-F "file=@./photo.png"Response
{
"id": "fil_...",
"filename": "photo.png",
"url": "/api/v1/files/fil_..."
}/api/v1/files/:idStreams the raw file bytes with the stored Content-Type. Because it is Bearer-gated you cannot put it directly in an `<img src>`; fetch it with the token and build an object URL client-side.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id returned by the upload endpoint. |
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here" \
--output downloaded-fileResponse
Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown./api/v1/files/:idDeletes a file owned by the calling key.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id to delete. |
Request
curl -X DELETE https://ypk0ho4z.vibecode.cloud/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here"Response
{ "deleted": true } // { "deleted": false } with status 404 if not found / not owned/api/v1/pilotsA pilot is one target website/SaaS you drive with SaaSPilot. Returns every pilot visible to the key: the key owner's own pilots (or its tenant's in multi-tenant mode) plus pilots other people shared with the key owner. Each has an action count, the caller's `role` (`owner` | `admin` | `editor` | `user`), `shared` and `sharedBy` (the owner's e-mail for shared pilots). Every role can read pilots/actions/runs and run actions through this API. Stored site credentials are never returned — only `hasCredentials`.
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/pilots \
-H "Authorization: Bearer sk_your_key_here"Response
{
"pilots": [
{
"id": "k2j4h5...",
"name": "Acme Shop",
"baseUrl": "https://shop.acme.test/",
"description": "Our supplier's web shop",
"hasCredentials": true,
"actionCount": 3,
"role": "owner",
"shared": false,
"sharedBy": null,
"createdAt": "2026-10-03T10:00:00.000Z",
"updatedAt": "2026-10-03T10:00:00.000Z"
},
{
"id": "p9q8r7...",
"name": "Partner portal",
"baseUrl": "https://portal.partner.test/",
"description": "",
"hasCredentials": true,
"actionCount": 1,
"role": "user",
"shared": true,
"sharedBy": "owner@partner.test",
"createdAt": "2026-10-02T09:00:00.000Z",
"updatedAt": "2026-10-02T09:00:00.000Z"
}
]
}/api/v1/pilots/:idOne pilot (owned, tenant or shared with the key owner — with `role`, `shared`, `sharedBy` as in the list) plus all of its actions (same shape as `GET /api/v1/actions/:id`).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Pilot id. |
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/pilots/PILOT_ID \
-H "Authorization: Bearer sk_your_key_here"Response
{ "pilot": { "id": "...", "name": "Acme Shop", "role": "editor", "shared": true, "sharedBy": "owner@acme.test", ... }, "actions": [ { "id": "...", "name": "Search products", ... } ] }
// 404 { "error": "Pilot not found" }/api/v1/actionsAn action is one automation on a pilot's site (e.g. "search products for {query}"), written by AI as a versioned recipe of validated steps. `status` is `authoring` | `ready` | `failed` | `broken` | `repairing`; only actions with an `activeVersion` can run. `health` reflects the daily health check (`unknown` | `passed` | `failed` | `repaired` | `broken`). Includes the actions of pilots shared with the key owner.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| pilotId | query | string | no | Only actions of this pilot. |
Request
curl "https://ypk0ho4z.vibecode.cloud/api/v1/actions?pilotId=PILOT_ID" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"actions": [
{
"id": "a8d9f0...",
"pilotId": "k2j4h5...",
"name": "Search products",
"goal": "Search the shop for {query} and list name, price, stock and URL",
"mode": "auto",
"inputFields": [ { "name": "query", "type": "string", "required": true } ],
"status": "ready",
"activeVersion": 2,
"latestVersion": 2,
"needsBrowser": false,
"health": { "status": "passed", "checkedAt": "2026-10-03T06:00:00.000Z", "message": "Health check passed." },
"job": { "id": "job_...", "kind": "refine", "status": "completed", "error": null, "startedAt": "2026-10-03T10:01:00.000Z" },
"lastRunAt": "2026-10-03T10:05:00.000Z",
"createdAt": "...",
"updatedAt": "..."
}
]
}/api/v1/actions/:idOne action (see `GET /api/v1/actions` for the fields).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Action id. |
Request
curl https://ypk0ho4z.vibecode.cloud/api/v1/actions/ACTION_ID \
-H "Authorization: Bearer sk_your_key_here"Response
{ "action": { "id": "a8d9f0...", "name": "Search products", "status": "ready", ... } }
// 404 { "error": "Action not found" }/api/v1/actions/:id/runRuns the action now (synchronously — usually seconds, browser-mode actions up to ~2 minutes) with the given input and returns the run with its data and per-step validation results. Stored pilot credentials are added to the input server-side. Pass `format` to get the data rendered as csv/html/xml/md/text instead of the JSON run object.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Action id. |
| input | body | object | no | Input fields as declared in the action's `inputFields`, e.g. `{"query":"shoes"}`. |
| version | body | number | no | Run a specific recipe version instead of the active one. |
| format | body | string | no | `json` (default: the run object) or `csv` | `html` | `xml` | `md` | `text` (just the rendered data). |
Request
curl -X POST https://ypk0ho4z.vibecode.cloud/api/v1/actions/ACTION_ID/run \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"input":{"query":"shoes"}}'Response
{
"run": {
"id": "r1x...",
"actionId": "a8d9f0...",
"trigger": "api",
"status": "passed",
"version": 2,
"input": { "query": "shoes" },
"data": [ { "name": "Trail Runner", "price": 89.95, "inStock": true, "url": "https://shop.acme.test/p/1001" } ],
"steps": [ { "id": "fetch_page", "ok": true, "errors": [], "ms": 412 }, { "id": "parse_items", "ok": true, "errors": [], "ms": 38 } ],
"failedStep": null,
"error": null,
"durationMs": 450,
"createdAt": "2026-10-03T10:05:00.000Z"
}
}
// 502 with the same body and "status": "failed" when a step fails its validator
// 409 { "error": "This action is still being authored — ..." } when it has no version yet/api/v1/actions/:id/runsNewest first; includes runs from the dashboard, the API, published endpoints, webhooks and assistants (`trigger`).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | Action id. |
| limit | query | number | no | 1–100, default 20. |
| data | query | string | no | `0` to omit the `data` payloads. |
Request
curl "https://ypk0ho4z.vibecode.cloud/api/v1/actions/ACTION_ID/runs?limit=5&data=0" \
-H "Authorization: Bearer sk_your_key_here"Response
{ "runs": [ { "id": "r1x...", "trigger": "endpoint", "status": "passed", "version": 2, "steps": [ ... ], "durationMs": 450, "createdAt": "..." } ] }