API Reference

Complete reference for ContainerPub backend API endpoints.

Authentication

ContainerPub uses Keycloak OIDC for authentication. Tokens are obtained via the Direct Grant flow and validated via token introspection.

For detailed authentication documentation, see Authentication System.

POST /api/auth/register

Register a new user account. Creates user in both Keycloak and PostgreSQL.

Request:

json
{
  "email": "user@example.com",
  "password": "securepassword",
  "first_name": "John",
  "last_name": "Doe"
}

Response:

json
{
  "message": "Account created successfully"
}

Flow:

  • Validate email format and password
  • Check registration feature flag (early access gating)
  • Create user in Keycloak via Admin API (createUserWithCredentials)
  • Store user in PostgreSQL with keycloak_user_id
  • Store user_information in PostgreSQL
  • Sync attributes to Keycloak
  • Return success message

Notes:

  • Password is managed by Keycloak (not stored in PostgreSQL)
  • Email must be unique
  • No tokens returned — user must login after registration

POST /api/auth/login

Authenticate via Keycloak and receive tokens. Legacy BCrypt users are auto-migrated to Keycloak.

Request:

json
{
  "email": "user@example.com",
  "password": "securepassword"
}

Response:

json
{
  "access_token": "eyJhbGc...",
  "refresh_token": "eyJhbGc...",
  "expires_in": 300,
  "token_type": "Bearer"
}

Flow:

  • Try Keycloak Direct Grant login
  • If successful: look up user by keycloak_user_id, check status, return tokens
  • If Keycloak fails: try BCrypt fallback (legacy users)
  • If BCrypt succeeds: auto-create user in Keycloak, backfill keycloak_user_id, sync attributes, login via Keycloak
  • If both fail: return "Invalid credentials"

Status Check: Blocked or deleted users are rejected during login.

POST /api/auth/refresh

Refresh an expired access token using Keycloak's refresh_token grant.

Request:

json
{
  "refresh_token": "eyJhbGc..."
}

Response:

json
{
  "access_token": "eyJhbGc...",
  "refresh_token": "eyJhbGc...",
  "expires_in": 300,
  "token_type": "Bearer"
}

POST /api/auth/logout

Logout and invalidate session via Keycloak.

Headers:

yaml
Authorization: Bearer <access-token>

Request:

json
{
  "refresh_token": "eyJhbGc..."
}

Response:

json
{
  "message": "Logout successful"
}

POST /api/auth/reset-password

Initiate password reset via Keycloak native flow.

Request:

json
{
  "email": "user@example.com"
}

Response:

json
{
  "message": "If the email exists, a password reset link has been sent"
}

API Keys

API keys provide an additional layer of security for function invocations using HMAC-SHA256 signatures.

For detailed documentation on API keys, see API Keys & Signing.

POST /api/apikey/generate

Generate a new API key for a function.

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: application/json

Request:

json
{
  "function_id": "function-uuid",
  "validity": "1d",  // Options: 1h, 1d, 1w, 1m, forever
  "name": "Optional key name"
}

Response:

json
{
  "message": "API key generated successfully",
  "warning": "Store the private_key securely - it will not be shown again!",
  "api_key": {
    "uuid": "key-uuid",
    "public_key": "base64-public-key",
    "private_key": "base64-private-key",
    "validity": "1d",
    "expires_at": "2025-12-16T02:00:00Z",
    "created_at": "2025-12-15T02:00:00Z"
  }
}

GET /api/apikey/:function_id

Get API key info for a function.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "has_api_key": true,
  "api_key": {
    "uuid": "key-uuid",
    "public_key": "base64-public-key",
    "validity": "1d",
    "is_active": true,
    "expires_at": "2025-12-16T02:00:00Z"
  }
}

DELETE /api/apikey/:api_key_uuid/revoke

Revoke an API key (deactivate but keep in history).

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "message": "API key revoked successfully"
}

What it does:

  • Marks the API key as inactive
  • Records the revocation timestamp
  • Key remains in database for audit history
  • Cannot be reactivated

DELETE /api/apikey/:api_key_uuid

Delete an API key (remove from database).

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "message": "API key revoked successfully"
}

What it does:

  • Permanently removes the API key from the database
  • No audit trail remains
  • Cannot be recovered

GET /api/apikey/:function_id/list

List all API keys for a function.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "api_keys": [
    {
      "uuid": "key-uuid",
      "validity": "1d",
      "is_active": true,
      "expires_at": "2025-12-16T02:00:00Z"
    }
  ]
}

PUT /api/apikey/:api_key_uuid/roll

Extend an API key's expiration by its validity period.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "message": "API key updated successfully"
}

What it does:

  • Extends the expiration date by the key's validity period
  • For example, if validity is 1d, it adds 1 day to the current expiration
  • Useful for extending active keys without regenerating them
  • Does not change the key UUID or secret

PUT /api/apikey/:api_key_uuid/update

Update an API key's validity period.

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: application/json

Request:

json
{
  "validity": "1w",  // New validity: 1h, 1d, 1w, 1m, forever
  "name": "Updated key name"  // Optional
}

Response:

json
{
  "message": "API key updated successfully"
}

PUT /api/apikey/:api_key_uuid/enable

Re-enable a revoked API key.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "message": "API key enabled successfully"
}

Notes:

  • Only works on revoked keys (is_active = false)
  • Sets is_active to true
  • Does not change expiration date

Token Security

Tokens are issued and managed by Keycloak. The platform backend validates tokens via Keycloak's introspection endpoint on every request. A legacy JWT fallback exists during the migration period.

For details, see Authentication System.

Functions API

POST /api/functions/init

Initialize a new function (creates UUID and database record).

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: application/json

Request:

json
{
  "name": "my_function",
  "skip_signing": false  // Optional, default: false
}

Response (New Function):

json
{
  "message": "Function initialized successfully",
  "id": "function-uuid",
  "name": "my_function",
  "status": "init",
  "skip_signing": false,
  "created_at": "2025-01-01T00:00:00Z"
}

Response (Existing Function):

json
{
  "message": "Function already exists",
  "id": "function-uuid",
  "name": "my_function",
  "status": "active",
  "skip_signing": false,
  "deployment_version": 3,
  "created_at": "2025-01-01T00:00:00Z",
  "already_exists": true
}

Notes:

  • Must be called before deployment
  • Creates function record with status init
  • Returns existing function if name already exists for user
  • Generates unique UUID for the function

POST /api/functions/deploy

Deploy a new function or update existing one.

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: multipart/form-data

Form Data:

  • function_id - Function UUID (from init endpoint)
  • archive - Function archive (tar.gz)
  • env - Environment variables (JSON, optional)

Response:

json
{
  "id": "function-id",
  "name": "my-function",
  "status": "deployed",
  "url": "https://api.containerpub.dev/functions/function-id"
}

GET /api/functions

List all deployed functions for authenticated user.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • limit - Number of results (default: 10)
  • offset - Pagination offset (default: 0)

Response:

json
{
  "functions": [
    {
      "id": "function-id",
      "name": "my-function",
      "status": "active",
      "created_at": "2025-01-01T00:00:00Z"
    }
  ],
  "total": 1
}

GET /api/functions/:id

Get function details.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "id": "function-id",
  "name": "my-function",
  "status": "active",
  "memory": 512,
  "timeout": 30,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

DELETE /api/functions/:id

Delete a function.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "message": "Function deleted successfully"
}

Function Execution

POST /api/functions/:id/invoke

Invoke a deployed function.

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: application/json

Request:

json
{
  "body": {
    "key": "value"
  }
}

Response:

json
{
  "execution_id": "exec-id",
  "status": "success",
  "result": {
    "output": "result"
  },
  "duration_ms": 150
}

GET /api/functions/:id/executions

Get execution history for a function with pagination.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • page - Page number (default: 1)
  • per_page - Results per page (default: 20, max: 100)

Response:

json
{
  "executions": [
    {
      "uuid": "exec-uuid",
      "status": "success",
      "started_at": "2025-01-01T00:00:00Z",
      "completed_at": "2025-01-01T00:00:05Z",
      "duration_ms": 150
    }
  ],
  "page": 1,
  "per_page": 20,
  "total": 45,
  "has_next": true
}

GET /api/functions/:id/executions/:executionUuid

Get detailed information about a specific execution.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "execution": {
    "uuid": "exec-uuid",
    "function_uuid": "function-uuid",
    "status": "success",
    "started_at": "2025-01-01T00:00:00Z",
    "completed_at": "2025-01-01T00:00:05Z",
    "duration_ms": 150,
    "invocation": {
      "uuid": "invocation-uuid",
      "timestamp": "2025-01-01T00:00:00Z",
      "success": true,
      "duration_ms": 150,
      "error_message": null
    },
    "logs": [
      {
        "timestamp": "2025-01-01T00:00:01Z",
        "level": "info",
        "message": "Function started"
      }
    ]
  }
}

Versioning & Rollback

GET /api/functions/:id/deployments

Get deployment history for a function with pagination.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • page - Page number (default: 1)
  • per_page - Results per page (default: 20, max: 100)

Response:

json
{
  "function_uuid": "function-uuid",
  "function_name": "my-function",
  "function_status": "active",
  "page": 1,
  "per_page": 20,
  "count": 5,
  "total_pages": 1,
  "has_next": false,
  "deployments": [
    {
      "uuid": "deployment-uuid",
      "version": 3,
      "image_tag": "registry/my-function:v3",
      "s3_key": "functions/uuid/v3.tar.gz",
      "status": "active",
      "is_active": true,
      "created_at": "2025-01-03T00:00:00Z",
      "deployed_at": "2025-01-03T00:01:00Z"
    },
    {
      "uuid": "deployment-uuid-2",
      "version": 2,
      "image_tag": "registry/my-function:v2",
      "s3_key": "functions/uuid/v2.tar.gz",
      "status": "inactive",
      "is_active": false,
      "created_at": "2025-01-02T00:00:00Z",
      "deployed_at": "2025-01-02T00:01:00Z"
    }
  ]
}

POST /api/functions/:id/rollback

Rollback function to a previous deployment version.

Headers:

yaml
Authorization: Bearer <access-token>
Content-Type: application/json

Request:

json
{
  "version": 2  // Version number to rollback to
}

Response:

json
{
  "message": "Function rolled back successfully",
  "function": {
    "uuid": "function-uuid",
    "name": "my-function",
    "status": "active",
    "active_version": 2
  }
}

Notes:

  • Rollback restores the specified version as the active deployment
  • Previous active deployment is marked as inactive
  • Function container is restarted with the rolled-back version

Deletion Management

POST /api/functions/:uuid/cancel-deletion

Cancel a pending deletion for a function.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "success": true,
  "message": "Deletion cancelled successfully",
  "function": {
    "uuid": "function-uuid",
    "name": "my-function",
    "status": "active"
  }
}

Notes:

  • Cancels all pending image deletions for the function
  • Restores function status to active
  • Can only cancel deletions with status pending

GET /api/functions/pending-deletions

Get all pending deletions for the authenticated user.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "pending_deletions": [
    {
      "id": "pending-deletion-uuid",
      "function_uuid": "function-uuid",
      "function_name": "my-function",
      "function_status": "pending_deletion",
      "image_tag": "registry/my-function:v1",
      "reason": "stale_function",
      "deletion_date": "2025-02-01T00:00:00Z",
      "days_remaining": 7,
      "scheduled_at": "2025-01-25T10:30:00Z"
    }
  ],
  "count": 1
}

Notes:

  • Returns only deletions with status pending
  • Ordered by deletion date (earliest first)
  • days_remaining calculated from current date

GET /api/functions/:uuid/deletion-status

Get deletion status for a specific function.

Headers:

yaml
Authorization: Bearer <access-token>

Response:

json
{
  "function_uuid": "function-uuid",
  "function_name": "my-function",
  "has_pending_deletion": true,
  "deletion_info": {
    "id": "pending-deletion-uuid",
    "image_tag": "registry/my-function:v1",
    "reason": "stale_function",
    "deletion_date": "2025-02-01T00:00:00Z",
    "days_remaining": 7,
    "scheduled_at": "2025-01-25T10:30:00Z"
  }
}

Logs API

GET /api/functions/:id/logs

Get function execution logs.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • limit - Number of lines (default: 100)
  • since - Timestamp filter (ISO 8601)

Response:

json
{
  "logs": [
    {
      "timestamp": "2025-01-01T00:00:00Z",
      "level": "info",
      "message": "Function started"
    }
  ]
}

Error Responses

400 Bad Request

json
{
  "error": "Invalid request",
  "details": "Missing required field: name"
}

401 Unauthorized

json
{
  "error": "Unauthorized",
  "details": "Invalid or expired token"
}

Common Causes:

  • Missing Authorization header
  • Invalid access token
  • Expired access token (use refresh token to get new one)

403 Forbidden

json
{
  "error": "Forbidden",
  "details": "Access denied"
}

404 Not Found

json
{
  "error": "Not found",
  "details": "Function not found"
}

500 Internal Server Error

json
{
  "error": "Internal server error",
  "details": "An unexpected error occurred"
}

Rate Limiting

  • Requests per minute: 60
  • Requests per hour: 1000
  • Concurrent executions: 10 per function

Statistics API

GET /api/stats/overview

Get aggregated statistics for all user's functions.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • period - Time period: 1h, 24h, 7d, 30d (default: 24h)

Response:

json
{
  "total_functions": 5,
  "invocations_count": 1250,
  "success_count": 1245,
  "error_count": 5,
  "average_latency_ms": 120,
  "period": "24h"
}

GET /api/stats/overview/hourly

Get hourly chart data for all user's functions.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • hours - Number of hours (default: 24, max: 168)

Response:

json
{
  "data": [
    {
      "hour": "2024-01-15T10:00:00Z",
      "total_requests": 150,
      "success_count": 148,
      "error_count": 2,
      "average_latency_ms": 120
    }
  ],
  "hours": 24
}

GET /api/stats/overview/daily

Get daily chart data for all user's functions.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • days - Number of days (default: 30, max: 90)

Response:

json
{
  "data": [
    {
      "day": "2024-01-15T00:00:00Z",
      "total_requests": 1500,
      "success_count": 1495,
      "error_count": 5,
      "average_latency_ms": 115
    }
  ],
  "days": 30
}

GET /api/functions/:uuid/stats

Get statistics for a specific function.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • period - Time period: 1h, 24h, 7d, 30d (default: 24h)

Response:

json
{
  "invocations_count": 250,
  "success_count": 248,
  "error_count": 2,
  "average_latency_ms": 125,
  "period": "24h"
}

GET /api/functions/:uuid/stats/hourly

Get hourly chart data for a specific function.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • hours - Number of hours (default: 24, max: 168)

Response:

json
{
  "data": [
    {
      "hour": "2024-01-15T10:00:00Z",
      "total_requests": 25,
      "success_count": 24,
      "error_count": 1,
      "average_latency_ms": 125
    }
  ],
  "hours": 24
}

GET /api/functions/:uuid/stats/daily

Get daily chart data for a specific function.

Headers:

yaml
Authorization: Bearer <access-token>

Query Parameters:

  • days - Number of days (default: 30, max: 90)

Response:

json
{
  "data": [
    {
      "day": "2024-01-15T00:00:00Z",
      "total_requests": 250,
      "success_count": 248,
      "error_count": 2,
      "average_latency_ms": 125
    }
  ],
  "days": 30
}

For detailed documentation on statistics and monitoring, see Statistics & Monitoring.

Health Check

GET /health

Check backend server health.

Response:

json
{
  "status": "ok",
  "timestamp": "2025-01-01T00:00:00Z"
}

No authentication required.

Next Steps