HTTP API Reference

Complete reference for Vultrino's HTTP API endpoints.

Base URL

Default: http://127.0.0.1:7878

Authentication

API Key Authentication

Include the Vultrino API key in the Authorization header:

Authorization: Bearer vk_your_api_key_here

Use Token Authentication

The execute and approval-polling endpoints also accept a use token in the same Authorization: Bearer header. Use tokens are recognized by their vut_ prefix and carry their own credential/action scope and use limits — see Use Tokens.

Authorization: Bearer vut_your_use_token_here

No Authentication (Local Mode)

In local mode without RBAC, no authentication is required.


Proxy Endpoints

Execute Proxied Request

Proxy a request through Vultrino with automatic credential injection.

URL Format:

{method} /{target_url}

Headers:

HeaderRequiredDescription
X-Vultrino-CredentialYesAlias of credential to use
AuthorizationDependsAPI key (if RBAC enabled)
*NoAll other headers passed to target

Example:

GET /https://api.github.com/user HTTP/1.1
Host: localhost:7878
X-Vultrino-Credential: github-api
Accept: application/json

Response: Returns the response from the target server, including:

  • Status code
  • Headers
  • Body

Error Responses:

StatusCodeDescription
400missing_credentialX-Vultrino-Credential header not provided
401unauthorizedInvalid or expired API key
403forbiddenPermission denied by RBAC or policy
404not_foundCredential alias not found
502upstream_errorFailed to connect to target server

Execute API

POST /v1/execute

Execute an action with a credential. More flexible than direct proxy.

Request:

POST /v1/execute HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json

{
  "credential": "github-api",
  "action": "http.request",
  "params": {
    "method": "GET",
    "url": "https://api.github.com/user",
    "headers": {
      "Accept": "application/json"
    },
    "body": null
  }
}

Request Fields:

FieldTypeRequiredDescription
credentialstringYesCredential alias
actionstringYesAction to perform
paramsobjectYesAction-specific parameters

Actions:

ActionDescription
http.requestMake an HTTP request
crypto.signSign data (future)

HTTP Request Params:

FieldTypeRequiredDescription
methodstringYesHTTP method
urlstringYesTarget URL
headersobjectNoAdditional headers
bodystringNoRequest body

Response:

{
  "status": 200,
  "headers": {
    "content-type": "application/json",
    "x-ratelimit-remaining": "4999"
  },
  "body": "{\"login\":\"username\",...}"
}

Approval-gated response (202 Accepted):

If the credential, use token, or a policy requires human approval, the action does not run. Instead the endpoint returns the id to poll:

{
  "outcome": "pending_approval",
  "approval_id": "appr_xxxxxxxx",
  "message": "This action requires human approval before it runs. It has NOT executed. Poll GET /api/v1/approvals/{id} with your bearer token to retrieve the result once a human approves.",
  "summary": "http.request on deploy-hook",
  "expires_at": "2024-01-15T11:30:00Z"
}

Action Approvals API

GET /api/v1/approvals/

Poll an approval and, once it has been approved, run the action and return its result. Execution is lazy and happens on this call. Authenticate with the same API key or use token that opened the approval — a caller may only poll its own approvals.

Request:

GET /api/v1/approvals/appr_xxxxxxxx HTTP/1.1
Host: localhost:7879
Authorization: Bearer vk_xxx

Response (still pending):

{
  "approval_id": "appr_xxxxxxxx",
  "status": "Pending",
  "summary": "http.request on deploy-hook",
  "executed": false,
  "message": "Awaiting human approval. The action has NOT run. Poll this endpoint again every ~10-30 seconds with your bearer token.",
  "expires_at": "2024-01-15T11:30:00Z"
}

Response (approved and executed):

{
  "approval_id": "appr_xxxxxxxx",
  "status": "Approved",
  "summary": "http.request on deploy-hook",
  "executed": true,
  "message": "Approved and executed.",
  "result": {
    "status": 200,
    "body": "..."
  }
}

A Denied or Expired status returns a message telling the agent to stop and not retry. The action is run at most once across all polls.

Error Responses:

StatusCodeDescription
401invalid_token / invalid_api_keyMissing or invalid bearer token
403not_authorizedThe approval belongs to a different principal
403token_revokedThe polling use token has been revoked
404approval_not_foundNo such approval id

Credential Management API

GET /v1/credentials

List all credentials (metadata only, no secrets).

Request:

GET /v1/credentials HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "credentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "alias": "github-api",
      "type": "api_key",
      "description": "GitHub personal access token",
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    },
    {
      "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "alias": "stripe-api",
      "type": "api_key",
      "description": null,
      "created_at": "2024-01-16T14:20:00Z",
      "updated_at": "2024-01-16T14:20:00Z"
    }
  ]
}

Required Permission: read


GET /v1/credentials/

Get details about a specific credential.

Request:

GET /v1/credentials/github-api HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "alias": "github-api",
  "type": "api_key",
  "description": "GitHub personal access token",
  "metadata": {},
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z"
}

Required Permission: read

Error Responses:

StatusCodeDescription
404not_foundCredential not found

POST /v1/credentials

Create a new credential.

Request:

POST /v1/credentials HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json

{
  "alias": "new-api-key",
  "type": "api_key",
  "data": {
    "key": "secret_key_value"
  },
  "description": "Description of this credential",
  "metadata": {
    "team": "backend"
  }
}

Request Fields:

FieldTypeRequiredDescription
aliasstringYesUnique human-readable name
typestringYesCredential type
dataobjectYesCredential data (type-specific)
descriptionstringNoOptional description
metadataobjectNoCustom metadata

Credential Types and Data:

api_key:

{
  "type": "api_key",
  "data": {
    "key": "your_api_key"
  }
}

basic_auth:

{
  "type": "basic_auth",
  "data": {
    "username": "user",
    "password": "pass"
  }
}

oauth2:

{
  "type": "oauth2",
  "data": {
    "client_id": "xxx",
    "client_secret": "xxx",
    "refresh_token": "xxx",
    "access_token": "xxx",
    "expires_at": "2024-02-15T10:30:00Z"
  }
}

Response:

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "alias": "new-api-key"
}

Required Permission: write

Error Responses:

StatusCodeDescription
400invalid_requestInvalid request body
409conflictAlias already exists

DELETE /v1/credentials/

Delete a credential.

Request:

DELETE /v1/credentials/old-api HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "success": true
}

Required Permission: delete

Error Responses:

StatusCodeDescription
404not_foundCredential not found

Role Management API

GET /v1/roles

List all roles.

Request:

GET /v1/roles HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "roles": [
    {
      "id": "role-123",
      "name": "executor",
      "description": "Can execute requests",
      "permissions": ["read", "execute"],
      "credential_scopes": [],
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
}

POST /v1/roles

Create a new role.

Request:

POST /v1/roles HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json

{
  "name": "github-reader",
  "description": "Read-only GitHub access",
  "permissions": ["read", "execute"],
  "credential_scopes": ["github-*"]
}

Response:

{
  "id": "role-456",
  "name": "github-reader"
}

DELETE /v1/roles/

Delete a role.

Request:

DELETE /v1/roles/old-role HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "success": true
}

API Key Management

GET /v1/keys

List all API keys (shows prefix only, not full key).

Request:

GET /v1/keys HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "keys": [
    {
      "id": "key-123",
      "name": "my-app",
      "key_prefix": "vk_a1b2c3d4",
      "role_id": "role-123",
      "expires_at": null,
      "last_used_at": "2024-01-16T14:20:00Z",
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
}

POST /v1/keys

Create a new API key.

Request:

POST /v1/keys HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json

{
  "name": "new-app-key",
  "role_id": "role-123",
  "expires_at": "2024-12-31T23:59:59Z"
}

Response:

{
  "id": "key-456",
  "key": "vk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "name": "new-app-key"
}

Note: The full key is only returned once at creation. Store it securely.


DELETE /v1/keys/

Revoke an API key.

Request:

DELETE /v1/keys/key-123 HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx

Response:

{
  "success": true
}

Health Check

GET /health

Check if the server is running.

Request:

GET /health HTTP/1.1
Host: localhost:7878

Response:

{
  "status": "ok",
  "version": "0.1.0"
}

No authentication required.


Error Response Format

All errors follow this format:

{
  "error": "error_code",
  "message": "Human-readable error message",
  "details": {}
}

Common Error Codes

CodeHTTP StatusDescription
invalid_request400Malformed request body or parameters
missing_credential400X-Vultrino-Credential header missing
unauthorized401Invalid or expired API key
forbidden403Permission denied
not_found404Resource not found
conflict409Resource already exists
rate_limited429Too many requests
internal_error500Server error
upstream_error502Target server error

Rate Limits

Default rate limits (configurable):

EndpointLimit
Proxy requests1000/minute
Credential management100/minute
Authentication10 failed/minute

Rate limit headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1705330800