Provisioning API
Programmatically provision agents, manage zero-knowledge secrets, and inject custom build scripts for enterprise deployments.
Base URL: https://api.maritime.sh/api/v1
Auth: All provisioning endpoints require an X-API-Key header with a maritime API key (mk_...).
Install
You need a NaCl / libsodium library to seal secrets. Everything else uses standard HTTP.
pip install PyNaCl requestsQuick Start
Provision an agent, seal secrets, and deploy — in under 30 lines.
# Set your API key
export MARITIME_API_KEY="mk_your_key_here"
# 1. Provision an agent
curl -s -X POST https://api.maritime.sh/api/v1/provision \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "my-agent",
"image": "openclaw",
"image_name": "python:3.11-slim",
"tier": "always_on",
"auto_deploy": false
}'
# Save the agent_id and public_key from the response, then:
# 2. Deploy the agent
curl -s -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/deploy \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json"
# 3. Check status
curl -s https://api.maritime.sh/api/v1/agents/AGENT_ID \
-H "X-API-Key: $MARITIME_API_KEY"How Sealed Secrets Work
Maritime uses X25519 + XSalsa20-Poly1305 (NaCl sealed boxes) for zero-knowledge secret management. Neither the API caller nor Maritime can read the secrets after submission.
You (API caller) Maritime Platform Container
│ │ │
│ POST /provision ────────────►│ │
│ │ generate X25519 keypair │
│◄── { public_key } ──────────│ │
│ │ │
│ seal(secret, public_key) │ │
│ POST /secrets ──────────────►│ store sealed blob │
│ │ (cannot decrypt) │
│ │ │
│ POST /deploy ───────────────►│ deploy ─────────────────►│
│ │ inject sealed_secrets │
│ │ inject private_key │
│ │ │
│ │ unseal.py decrypts
│ │ private_key deleted
│ │ secrets → env varsImportant: The private key exists only inside the container and is deleted after secrets are unsealed. You can always add more secrets — they merge with existing ones and unseal on the next deploy/restart.
API Key Management
Create and manage API keys from the dashboard. These endpoints use session authentication (cookies), not API key auth.
Create API Key
/api/v1/keys| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable name for this key |
| scopes | string[] | No | Scopes: provision, deploy, secrets, manage. Default: all. |
| expires_in_days | integer | null | No | Key expiry in days. Null = never expires. |
curl -X POST https://api.maritime.sh/api/v1/keys \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "production-pipeline",
"scopes": ["provision", "deploy", "secrets", "manage"],
"expires_in_days": 90
}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "production-pipeline",
"key_prefix": "mk_aBcDeFgH",
"scopes": ["provision", "deploy", "secrets", "manage"],
"is_active": true,
"last_used_at": null,
"expires_at": "2026-06-13T10:30:00Z",
"created_at": "2026-03-15T10:30:00Z",
"raw_key": "mk_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789abc"
}Store the raw_key securely. It is returned only once at creation time and cannot be retrieved again.
List API Keys
/api/v1/keyscurl -s https://api.maritime.sh/api/v1/keys \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"Revoke API Key
/api/v1/keys/{key_id}curl -X DELETE https://api.maritime.sh/api/v1/keys/KEY_ID \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"
# Returns 204 No Content on successProvisioning
Provision Agent
/api/v1/provisionscope: provision| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Agent name |
| description | string | No | Agent description |
| image | string | No | Framework: openclaw, crewai, langgraph, docker, custom, auto |
| image_name | string | No | Docker image (e.g. python:3.11-slim) |
| repo_url | string | No | GitHub repo URL for source builds |
| branch | string | No | Git branch (default: main) |
| tier | string | No | Tier: smart, extended, always_on |
| auto_deploy | boolean | No | Deploy immediately (default: true) |
| env_vars | object | No | Non-secret env vars as key-value pairs |
curl -X POST https://api.maritime.sh/api/v1/provision \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "customer-support-bot",
"image": "openclaw",
"image_name": "python:3.11-slim",
"repo_url": "https://github.com/acme/support-agent",
"tier": "always_on",
"auto_deploy": true,
"env_vars": {
"LOG_LEVEL": "info",
"REGION": "us-east-1"
}
}'{
"agent_id": "7f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"name": "customer-support-bot",
"status": "deploying",
"public_key": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"deployment_id": "d8e9f0a1-b2c3-4d5e-6f7a-8b9c0d1e2f3a",
"message": "Agent provisioned. Use the public_key to seal secrets with NaCl crypto_box_seal."
}List Agents
/api/v1/agentsscope: provisioncurl -s https://api.maritime.sh/api/v1/agents \
-H "X-API-Key: $MARITIME_API_KEY"Get Agent Status
/api/v1/agents/{agent_id}scope: provisioncurl -s https://api.maritime.sh/api/v1/agents/AGENT_ID \
-H "X-API-Key: $MARITIME_API_KEY"Sealed Secrets
Upload Sealed Secrets
/api/v1/agents/{agent_id}/secretsscope: secretsEach value must be encrypted client-side using NaCl crypto_box_seal with the agent's public_key, then base64-encoded.
| Parameter | Type | Required | Description |
|---|---|---|---|
| secrets | object | Yes | Map of KEY → base64-encoded sealed ciphertext |
# Sealing requires a crypto library — use this Python one-liner to seal,
# then pipe to curl:
python3 -c "
from nacl.public import PublicKey, SealedBox
import base64, json
PUBLIC_KEY = 'YOUR_PUBLIC_KEY_HEX'
pk = PublicKey(bytes.fromhex(PUBLIC_KEY))
box = SealedBox(pk)
secrets = {}
for k, v in {
'OPENAI_API_KEY': 'sk-proj-...',
'DATABASE_URL': 'postgresql://user:pass@host/db',
'STRIPE_SECRET': 'sk_live_...',
}.items():
secrets[k] = base64.b64encode(box.encrypt(v.encode())).decode()
print(json.dumps({'secrets': secrets}))
" | curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/secrets \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d @-Custom Build Files
Upload Custom Files
/api/v1/agents/{agent_id}/filesscope: deployInject files into the agent container. By default, files are placed in /maritime/scripts/ and shell scripts are executed after deploy. You can customize the target directory and control whether scripts run on deploy.
| Parameter | Type | Required | Description |
|---|---|---|---|
| files | CustomFile[] | Yes | Array of files to inject |
| files[].path | string | Yes | Filename or relative path (e.g. install-tools.sh, config/app.toml) |
| files[].content | string | Yes | File content. Include shebang for scripts. |
| files[].executable | boolean | No | chmod +x the file (default: true) |
| files[].run_on_deploy | boolean | No | Execute .sh files after deploy (default: true). Set false to only place the file. |
| files[].target_dir | string | No | Directory inside the container (default: /maritime/scripts). E.g. /app/config, /data. |
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/files \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"files": [
{
"path": "install-deps.sh",
"content": "#!/bin/bash\nset -e\napt-get update -qq\napt-get install -y -qq ffmpeg imagemagick\npip install -q pandas numpy scikit-learn",
"executable": true,
"run_on_deploy": true,
"target_dir": "/maritime/scripts"
},
{
"path": "app.toml",
"content": "[server]\nhost = \"0.0.0.0\"\nport = 8080",
"executable": false,
"run_on_deploy": false,
"target_dir": "/app/config"
}
]
}'Lifecycle Management
Deploy / Redeploy
/api/v1/agents/{agent_id}/deployscope: deploy| Parameter | Type | Required | Description |
|---|---|---|---|
| image_name | string | No | Override Docker image |
| repo_url | string | No | Override repo URL |
| branch | string | No | Override git branch |
# Deploy with defaults (uses provisioned settings)
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/deploy \
-H "X-API-Key: $MARITIME_API_KEY"
# Deploy a specific branch
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/deploy \
-H "X-API-Key: $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"branch": "staging"}'Start / Stop / Restart
/api/v1/agents/{agent_id}/startscope: deploy/api/v1/agents/{agent_id}/stopscope: deploy/api/v1/agents/{agent_id}/restartscope: deploy# Start
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/start \
-H "X-API-Key: $MARITIME_API_KEY"
# Stop
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/stop \
-H "X-API-Key: $MARITIME_API_KEY"
# Restart
curl -X POST https://api.maritime.sh/api/v1/agents/AGENT_ID/restart \
-H "X-API-Key: $MARITIME_API_KEY"Teardown
/api/v1/agents/{agent_id}scope: manageStops the container, removes the volume, and deletes the agent record.
curl -X DELETE https://api.maritime.sh/api/v1/agents/AGENT_ID \
-H "X-API-Key: $MARITIME_API_KEY"
# Returns 204 No Content on successLogs & Deployments
Get Agent Logs
/api/v1/agents/{agent_id}/logs| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Max entries to return (default: 100, max: 1000) |
| level | string | No | Filter by level: info, warning, error |
curl -s "https://api.maritime.sh/api/v1/agents/AGENT_ID/logs?limit=50&level=error" \
-H "X-API-Key: $MARITIME_API_KEY"[
{
"id": "a1b2c3d4...",
"level": "error",
"message": "Container exited with code 1",
"source": "system",
"timestamp": "2026-03-22T14:30:00Z"
}
]Get Deployment History
/api/v1/agents/{agent_id}/deployments| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Max entries to return (default: 10, max: 50) |
curl -s "https://api.maritime.sh/api/v1/agents/AGENT_ID/deployments?limit=5" \
-H "X-API-Key: $MARITIME_API_KEY"[
{
"id": "d8e9f0a1...",
"status": "success",
"source": "docker",
"branch": null,
"build_log": "Pulling image ghcr.io/openclaw/openclaw:latest...\nContainer created: abc123...\nCustom files injected: install-deps.sh → /maritime/scripts (executed)\nDeploy complete.",
"started_at": "2026-03-22T14:00:00Z",
"completed_at": "2026-03-22T14:00:45Z"
}
]Enterprise & Wallet
Enterprise accounts enable usage-based billing with wallet credits. These endpoints use session authentication (cookies).
Create Enterprise Account
/api/v1/enterprise/account| Parameter | Type | Required | Description |
|---|---|---|---|
| company_name | string | Yes | Company / organization name |
| rate_per_hour_cents | integer | No | Compute rate in cents per hour (default: 1 = $0.01/hr) |
| initial_balance_cents | integer | No | Initial wallet credit in cents (default: 0) |
curl -X POST https://api.maritime.sh/api/v1/enterprise/account \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_name": "Acme Corp",
"rate_per_hour_cents": 1,
"initial_balance_cents": 10000
}'{
"id": "ent_abc123...",
"company_name": "Acme Corp",
"balance_cents": 10000,
"balance_dollars": 100.0,
"rate_per_hour_cents": 1,
"rate_per_hour_dollars": 0.01,
"min_balance_cents": 0,
"auto_stop_on_zero": true,
"is_active": true,
"created_at": "2026-03-22T10:00:00Z"
}Get Enterprise Account
/api/v1/enterprise/accountcurl -s https://api.maritime.sh/api/v1/enterprise/account \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"Get Wallet Balance
/api/v1/wallet/balancecurl -s https://api.maritime.sh/api/v1/wallet/balance \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"{
"balance_cents": 8500,
"balance_dollars": 85.0,
"rate_per_hour_cents": 1,
"rate_per_hour_dollars": 0.01,
"period_compute_minutes": 1500.0,
"period_spend_cents": 1500,
"period_spend_dollars": 15.0,
"period_credits_cents": 10000,
"period_credits_dollars": 100.0,
"active_agents": 3,
"company_name": "Acme Corp"
}Top Up Wallet
/api/v1/wallet/topup| Parameter | Type | Required | Description |
|---|---|---|---|
| amount_cents | integer | Yes | Amount to add in cents (e.g. 10000 = $100). Must be > 0. |
curl -X POST https://api.maritime.sh/api/v1/wallet/topup \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"amount_cents": 10000}'Transaction History
/api/v1/wallet/transactions| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Max entries (default: 50) |
| offset | integer | No | Pagination offset (default: 0) |
curl -s "https://api.maritime.sh/api/v1/wallet/transactions?limit=10" \
-H "Authorization: Bearer YOUR_SESSION_TOKEN"API Key Scopes
| Scope | Grants Access To |
|---|---|
| provision | Provision new agents, list agents, get agent status |
| deploy | Deploy, start, stop, restart agents; upload custom files |
| secrets | Upload sealed secrets |
| manage | All of the above, plus teardown/delete agents |
Error Responses
| Status | Meaning |
|---|---|
| 401 | Missing, invalid, revoked, or expired API key |
| 403 | API key lacks the required scope for this endpoint |
| 402 | Insufficient wallet balance for deploy (enterprise accounts) |
| 404 | Agent not found, or agent belongs to a different user |
| 409 | Deploy already in progress for this agent |
Full End-to-End Example
Complete scripts you can copy, fill in your key, and run.
"""
maritime_provision.py — Provision, seal secrets, add build scripts, deploy.
pip install PyNaCl requests
"""
import base64
import requests
from nacl.public import PublicKey, SealedBox
API_KEY = "mk_your_key_here"
BASE = "https://api.maritime.sh/api/v1"
H = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
# ── 1. Provision ──
r = requests.post(f"{BASE}/provision", headers=H, json={
"name": "fraud-detector",
"image": "openclaw",
"image_name": "python:3.11-slim",
"repo_url": "https://github.com/acme/fraud-detector",
"tier": "always_on",
"auto_deploy": False,
"env_vars": {"LOG_LEVEL": "info", "REGION": "us-east-1"},
}).json()
agent_id = r["agent_id"]
public_key = r["public_key"]
print(f"1. Provisioned: {agent_id}")
# ── 2. Seal & upload secrets ──
pk = PublicKey(bytes.fromhex(public_key))
box = SealedBox(pk)
secrets = {}
for k, v in {
"OPENAI_API_KEY": "sk-proj-...",
"DATABASE_URL": "postgresql://user:pass@host:5432/db",
"WEBHOOK_SIGNING_SECRET": "whsec_...",
}.items():
secrets[k] = base64.b64encode(box.encrypt(v.encode())).decode()
r = requests.post(f"{BASE}/agents/{agent_id}/secrets", headers=H, json={"secrets": secrets}).json()
print(f"2. Secrets: {r['message']}")
# ── 3. Custom build files ──
r = requests.post(f"{BASE}/agents/{agent_id}/files", headers=H, json={
"files": [{
"path": "install-deps.sh",
"content": "#!/bin/bash\nset -e\napt-get update -qq\napt-get install -y -qq ffmpeg\npip install -q torch",
"executable": True,
"run_on_deploy": True,
}],
}).json()
print(f"3. Files: {r['message']}")
# ── 4. Deploy ──
r = requests.post(f"{BASE}/agents/{agent_id}/deploy", headers=H).json()
print(f"4. Deploy: {r['message']} (deployment_id: {r['deployment_id']})")
# ── 5. Poll status ──
import time
for _ in range(30):
status = requests.get(f"{BASE}/agents/{agent_id}", headers=H).json()["status"]
print(f" Status: {status}")
if status in ("active", "error"):
break
time.sleep(5)Client Libraries
For sealing secrets you need a NaCl / libsodium library:
Python: pip install PyNaCl
Node.js: npm install libsodium-wrappers
Go: go get golang.org/x/crypto/nacl/box
Rust: cargo add crypto_box
Ruby: gem install rbnacl