Skip to content

Latest commit

Β 

History

History
920 lines (714 loc) Β· 31.2 KB

File metadata and controls

920 lines (714 loc) Β· 31.2 KB

API

Important

All API requests must include an Authorization header in the following format: Authorization: Bearer <API_TOKEN>

All endpoints return JSON responses with appropriate HTTP status codes

Methods

/api/crons

Endpoint: /api/crons

Method: GET

Description: Returns the current settings for all cron jobs.

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/crons

Response Example:

[
  {
    "name": "collect",
    "schedule": "0 13 * * 6",
    "is_active": true,
    "updated_at": "2024-03-15T10:00:00Z"
  },
  {
    "name": "message",
    "schedule": "12 10 * * *",
    "is_active": true,
    "updated_at": "2024-03-15T10:00:00Z"
  }
]

/api/crons//schedule

Endpoint: /api/crons/{name}/schedule

Method: PUT

Description: Update the schedule for a specific cron job. The name can be either collect or message.

Curl Example:

curl -X PUT \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"schedule": "0 15 * * 6"}' \
  http://localhost:8080/api/crons/collect/schedule

Request Parameters:

Parameter Type Required Description
name string Yes Cron job name (collect or message)
schedule string Yes Cron schedule expression (e.g.,0 15 * * 6)

Request Example:

{
  "schedule": "0 15 * * 6"
}

Response Example:

{
  "status": "success",
  "message": "Schedule updated successfully"
}

/api/crons//status

Endpoint: /api/crons/{name}/status

Method: PUT

Description: Enable or disable a specific cron job. The name can be either collect or message.

Curl Example:

curl -X PUT \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"is_active": true}' \
  http://localhost:8080/api/crons/collect/status

Request Parameters:

Parameter Type Required Description
name string Yes Cron job name (collect or message)
is_active boolean Yes Enable (true) or disable (false) the job

Request Example:

{
  "is_active": false
}

Response Example:

{
  "status": "success",
  "message": "Status updated successfully"
}

/api/collect-settings

Endpoint: /api/collect-settings

Method: GET

Description: Returns the current settings for repository collection.

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/collect-settings

Response Example:

{
  "max_repos": 5,
  "resource": "github",
  "since": "daily",
  "spoken_language_code": "en",
  "period": "past_24_hours",
  "language": "All"
}

Note

All fields are stored in the database, but only relevant fields are used based on the resource value:

  • GitHub: uses since, spoken_language_code
  • OssInsight: uses period, language

/api/collect-settings (update)

Endpoint: /api/collect-settings

Method: PUT

Description: Update the repository collection settings.

Curl Example:

curl -X PUT \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "max_repos": 10,
    "resource": "github",
    "since": "weekly",
    "spoken_language_code": "uk",
    "period": "past_24_hours",
    "language": "All"
  }' \
  http://localhost:8080/api/collect-settings

Request Parameters:

Parameter Type Required Description
max_repos integer No Maximum number of repositories to collect
resource string No Data source:github (default) or ossinsight
since string No For GitHub: Time period (daily, weekly, monthly)
spoken_language_code string No For GitHub: Spoken language filter (e.g., en, uk, es)
period string No For OssInsight: Time period (past_24_hours, past_week, past_month, past_3_months)
language string No For OssInsight: Programming language filter (e.g., Python, All)

Request Example:

{
  "max_repos": 10,
  "resource": "github",
  "since": "weekly",
  "spoken_language_code": "uk",
  "period": "past_24_hours",
  "language": "All"
}

Response Example:

{
  "status": "success",
  "message": "Collect settings updated successfully"
}

/api/prompt-settings

Endpoint: /api/prompt-settings

Method: GET

Description: Returns the current AI prompt settings used for content generation.

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/prompt-settings

Response Example:

{
  "use_direct_url": true,
  "llm_provider": "openrouter",
  "temperature": 0.1,
  "model": "openai/gpt-4o-mini-search-preview",
  "content": "You are a helpful AI assistant that generates engaging content about software repositories.",
  "updated_at": "2024-03-15T10:00:00Z"
}

/api/prompt-settings (update)

Endpoint: /api/prompt-settings

Method: PUT

Description: Update the AI prompt settings for content generation.

Curl Example:

curl -X PUT \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "use_direct_url": false,
    "llm_provider": "anthropic",
    "temperature": 0.8,
    "model": "anthropic/claude-3-sonnet",
    "content": "You are an expert technical writer specializing in open-source projects."
  }' \
  http://localhost:8080/api/prompt-settings

Request Parameters:

Parameter Type Required Description
use_direct_url boolean No Whether to use direct URL for LLM API calls
llm_provider string No LLM provider name (e.g.,openai, mistral_agent, mistral_api, openrouter)
temperature float No Controls randomness in AI responses (0.0-2.0)
model string No The AI model to use for content generation
content string No The prompt content/template for AI generation

Request Example:

{
  "use_direct_url": false,
  "llm_provider": "openrouter",
  "temperature": 0.1,
  "model": "openai/gpt-4o-mini",
  "content": "You are an expert technical writer specializing in open-source projects."
}

Response Example:

{
  "status": "success",
  "message": "Prompt settings updated successfully"
}

/api/cron-history

Endpoint: /api/cron-history

Method: GET

Description: Retrieve the history of cron job executions with pagination, sorting, and filtering.

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?name=collect&page=1&limit=10"

Request Parameters:

Parameter Type Required Description
name string No Filter by cron job name (collect or message)
page integer No Page number (default: 1)
limit integer No Number of records per page (default: 20)
sort string No Sort order by execution date (asc or desc, default: desc)
status integer No Filter by execution status:0 (Failure), 1 (Success), 2 (Partial)
start_date string No Filter records from this date onwards (format:YYYY-MM-DD or RFC3339)
end_date string No Filter records up to this date (format:YYYY-MM-DD or RFC3339)

Response Structure:

The API returns a paginated response with the following structure. Note that the status field uses integer status codes:

  • 0: Failure
  • 1: Success
  • 2: Partial Success

Structure:

  • data: Array of cron history records
  • details: Present on message runs recorded after this field was introduced. Holds the item that was published and where it landed: url, sent, failed, and manual (true for runs triggered through /api/message/retry). Absent for older records and for collect runs.
  • pagination: Pagination metadata object containing:
    • total_count: Total number of records matching the filters
    • current_page: Current page number
    • total_pages: Total number of pages available
    • has_next: Boolean indicating if there's a next page
    • has_previous: Boolean indicating if there's a previous page

Response Example:

{
  "data": [
    {
      "name": "collect",
      "timestamp": "2024-03-15T10:00:00Z",
      "status": 1,
      "output": "Successfully collected 5 repositories"
    },
    {
      "name": "message",
      "timestamp": "2024-03-15T10:05:00Z",
      "status": 0,
      "output": "Network error"
    },
    {
      "name": "message",
      "timestamp": "2024-03-15T10:10:00Z",
      "status": 2,
      "output": "Message sent to: telegram. Failed: bluesky",
      "details": {
        "url": "https://github.com/resemble-ai/chatterbox",
        "sent": ["telegram"],
        "failed": ["bluesky"]
      }
    }
  ],
  "pagination": {
    "total_count": 25,
    "current_page": 1,
    "total_pages": 2,
    "has_next": true,
    "has_previous": false
  }
}

Usage Examples:

  1. Get all history (default: newest first, 20 records per page):
curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/cron-history
  1. Get history for specific job with custom pagination:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?name=collect&page=1&limit=10"
  1. Get only failed executions sorted by oldest first:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?status=0&sort=asc&limit=5"
  1. Get message job history with pagination and newest first sorting:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?name=message&page=2&limit=15&sort=desc"
  1. Get second page of all executions with 10 records per page:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?page=2&limit=10"
  1. Get history for a specific date range (from March 1st to March 15th, 2024):
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?start_date=2024-03-01&end_date=2024-03-15"
  1. Get failed executions from the last week:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?status=0&start_date=2024-03-08"
  1. Get collect job history for a specific date with precise timestamps:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?name=collect&start_date=2024-03-15T00:00:00Z&end_date=2024-03-15T23:59:59Z"
  1. Get recent executions from the last 3 days, sorted oldest first:
curl -H "Authorization: Bearer <API_TOKEN>" \
  "http://localhost:8080/api/cron-history?start_date=2024-03-12&sort=asc"

Date Range Filtering Notes:

  • Supported date formats:
    • Date only: YYYY-MM-DD (e.g., 2024-03-15)
    • RFC3339 with timezone: YYYY-MM-DDTHH:MM:SSZ (e.g., 2024-03-15T10:30:00Z)
  • Date validation:
    • Invalid date formats will return a 400 Bad Request error
    • If start_date is after end_date, the API will return a 400 Bad Request error
  • Date range behavior:
    • start_date is inclusive (records from this date onwards)
    • end_date is inclusive (records up to the end of this date)
    • When using date-only format, end_date includes the entire day (until 23:59:59.999...)
  • Timezone handling:
    • All timestamps are stored and compared in UTC
    • When using date-only format, the date is interpreted as the start of the day in UTC

Status Codes:

  • 200: Success
  • 400: Bad Request - Invalid parameters or date validation errors
  • 401: Unauthorized - Invalid or missing Bearer token
  • 500: Internal Server Error - Database or server error

/api/message/retry

Endpoint: /api/message/retry

Method: POST

Description: Re-send an already published repository to the integrations that did not receive it.

A message run marks the repository as posted as soon as any integration succeeds, which drops the item out of the publication queue. The connectors that failed can therefore never recover it on the next run β€” this endpoint is the way to finish such a partial publication by hand.

The repository text is fetched per integration in that integration's configured text_language, and one image is generated for the whole retry when any integration has socialify_image enabled. No Pushover notification is sent: a manual retry is already being watched by whoever triggered it.

Retries are serialised against each other, against /api/message/publish and against the message cron β€” a double-clicked button cannot publish twice, and a cron run cannot mark an item it never sent. Unlike publish-now, a retry waits for whatever holds that lock instead of refusing: it is a repair action, and refusing it would leave the connectors that failed unrepaired. The wait is unbounded and there is no request timeout, so a retry issued during a cron run can hold its connection for the length of that run (minutes, in the worst case described under /api/message/publish); repeated clicks queue up behind it rather than failing fast.

An item that is still unposted is marked as posted only when every requested integration succeeded; marking it after a partial retry would drop it out of the queue again, which is the failure this endpoint repairs.

Curl Example:

curl -X POST \
  'http://localhost:8080/api/message/retry' \
  -H 'Authorization: Bearer <API_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "apis": ["threads"],
    "url": "https://github.com/resemble-ai/chatterbox"
  }'

Request Parameters:

Parameter Type Required Description
apis string[] Yes Names of the integrations to send to, as configured in/api/api-configs. Blanks and duplicates are ignored.
url string No Repository to publish. When omitted the most recently published repository is used, which is only a guess at what a partial run consumed: a run that failed for every integration never marked its item as posted, so the guess resolves to the previous one. Callers that know the item β€” the dashboard reads it from the run details β€” should always pass it.

Response Structure:

  • url: The repository that was published
  • status: 0 (nothing sent), 1 (all sent), 2 (partially sent) β€” the same codes as cron history
  • message: The text recorded in cron history
  • succeeded / failed: Integration names per outcome
  • outcomes: Per-integration detail, with an error string for every failure
  • posted: Whether the item was marked as published and left the publication queue
  • posted_error: Present only when that marking failed β€” the item went out but stayed in the queue, so the scheduled run will publish it again

Response Example:

{
  "url": "https://github.com/resemble-ai/chatterbox",
  "status": 1,
  "message": "Manual retry: https://github.com/resemble-ai/chatterbox sent to: threads",
  "succeeded": ["threads"],
  "failed": null,
  "outcomes": [{ "api_name": "threads", "success": true }]
}

Every retry is recorded in cron history under the message name with details.manual = true, so /api/cron-history shows it alongside scheduled runs.

Status Codes:

  • 200: The retry ran. Individual integration failures are reported in outcomes, not in the status code
  • 400: Bad Request - Invalid body or an empty apis list
  • 401: Unauthorized - Invalid or missing Bearer token
  • 500: Internal Server Error - API configurations not loaded, or the repository could not be resolved

/api/message/publish

Endpoint: /api/message/publish

Method: POST

Description: Publish one repository to every enabled integration immediately, instead of waiting for its turn in the publication queue.

It exists because a post that is lost after publication β€” a bad record deleted from the queue once it was already sent β€” cannot be recovered by promoting anything: the item is gone from the queue. Promoting a repository (content-alchemist's /promote-repository/) only moves it to the head of the queue; this endpoint publishes it now.

There is no apis parameter: the run targets every integration whose api_configs row has enabled = true, resolved server-side at request time. A dashboard's cached configuration must not decide what actually gets published.

The repository text is fetched per integration in that integration's configured text_language, and one image is generated for the whole run when any integration has socialify_image enabled. No Pushover notification is sent β€” whoever triggered the run is watching it.

Unlike the message cron this does not revalidate the repository URL and delete dead ones: the cron picks blindly from the queue, while this publishes the row a human chose.

Marking as posted: the item is marked as published as soon as any integration accepts it, matching the message cron (and unlike /api/message/retry, which requires every requested integration to succeed). The run is recorded in cron history as a manual one, so the integrations that failed are finished off from there with the retry endpoint.

Concurrency: serialised against the message cron and the retry endpoint. Rather than queueing behind a cron run that can take minutes, it refuses with 409. A repository that is already published is refused with 409 as well, before any integration is contacted β€” a stale dashboard row must not publish the same post twice.

Duration: the request is synchronous and can take minutes β€” image generation retries for up to ~63 s, the Threads connector alone is configured with a 90 s timeout, and every integration adds its own repository lookup. Any reverse proxy in front of content-maestro needs a matching read timeout, or the browser gets a 504 while the publication keeps running.

Curl Example:

curl -X POST \
  'http://localhost:8080/api/message/publish' \
  -H 'Authorization: Bearer <API_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://github.com/resemble-ai/chatterbox"
  }'

Request Parameters:

Parameter Type Required Description
url string Yes Repository to publish. There is no fallback: "publish something now" is never a safe guess.

Response Structure: identical to /api/message/retry β€” url, status, message, succeeded, failed, outcomes, posted, posted_error.

Response Example:

{
  "url": "https://github.com/resemble-ai/chatterbox",
  "status": 2,
  "message": "Manual publish: https://github.com/resemble-ai/chatterbox sent to: telegram. Failed: threads. Errors: threads: API request failed with status 500",
  "succeeded": ["telegram"],
  "failed": ["threads"],
  "outcomes": [
    { "api_name": "telegram", "success": true },
    { "api_name": "threads", "success": false, "error": "API request failed with status 500" }
  ],
  "posted": true
}

Every run is recorded in cron history under the message name with details.manual = true, so /api/cron-history shows it alongside scheduled runs β€” and its details.failed list is what the retry endpoint works from.

Status Codes:

  • 200: The publication ran. Individual integration failures are reported in outcomes, not in the status code
  • 400: Bad Request - Invalid body or a missing url
  • 401: Unauthorized - Invalid or missing Bearer token
  • 409: Conflict - Another publication is already running, no integration is enabled, or the repository is already published
  • 500: Internal Server Error - API configurations not loaded, or the repository could not be resolved

/api/api-configs

Endpoint: /api/api-configs

Method: GET

Description: Retrieve all API configurations for external integrations (Twitter, Telegram, Bluesky, etc.).

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/api-configs

Response Example:

[
  {
    "id": 1,
    "name": "twitter",
    "url": "{env.TWITTER_URL}/x/api/posts/create",
    "method": "POST",
    "auth_type": "api_key",
    "token_env_var": "TWITTER_API_KEY",
    "token_header": "X-API-Key",
    "content_type": "multipart",
    "timeout": 30,
    "success_code": 200,
    "enabled": true,
    "response_type": "json",
    "text_language": "en",
    "socialify_image": false,
    "default_json_body": "",
    "updated_at": "2024-03-15T10:00:00Z"
  },
  {
    "id": 2,
    "name": "telegram",
    "url": "{env.TELEGRAM_SERVER_URL}/telegram/send-message",
    "method": "POST",
    "auth_type": "api_key",
    "token_env_var": "TELEGRAM_SERVER_TOKEN",
    "token_header": "X-API-Key",
    "content_type": "multipart",
    "timeout": 30,
    "success_code": 200,
    "enabled": true,
    "response_type": "json",
    "text_language": "uk",
    "socialify_image": true,
    "default_json_body": "",
    "updated_at": "2024-03-15T10:00:00Z"
  }
]

/api/api-configs/

Endpoint: /api/api-configs/{name}

Method: GET

Description: Retrieve a specific API configuration by name.

Curl Example:

curl -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/api-configs/twitter

Response Example:

{
  "id": 1,
  "name": "twitter",
  "url": "{env.TWITTER_URL}/x/api/posts/create",
  "method": "POST",
  "auth_type": "api_key",
  "token_env_var": "TWITTER_API_KEY",
  "token_header": "X-API-Key",
  "content_type": "multipart",
  "timeout": 30,
  "success_code": 200,
  "enabled": true,
  "response_type": "json",
  "text_language": "en",
  "socialify_image": false,
  "default_json_body": "",
  "updated_at": "2024-03-15T10:00:00Z"
}

/api/api-configs (create)

Endpoint: /api/api-configs

Method: POST

Description: Create a new API configuration for external integration.

Curl Example:

curl -X POST \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "bluesky",
    "url": "{env.BLUESKY_URL}/bluesky/api/posts/create",
    "method": "POST",
    "auth_type": "api_key",
    "token_env_var": "BLUESKY_SERVER_KEY",
    "token_header": "X-API-Key",
    "content_type": "multipart",
    "timeout": 30,
    "success_code": 200,
    "enabled": true,
    "response_type": "json",
    "text_language": "en",
    "socialify_image": false,
    "default_json_body": ""
  }' \
  http://localhost:8080/api/api-configs

Request Parameters:

Parameter Type Required Description
name string Yes Unique identifier for the API (alphanumeric, hyphens, underscores only)
url string Yes The endpoint URL (supports {env.VAR} syntax)
method string Yes HTTP method (GET, POST, PUT, DELETE, PATCH)
auth_type string No Authentication type:bearer, api_key, or empty
token_env_var string No Environment variable name containing the auth token
token_header string No Header name for API key (required if auth_type is api_key)
content_type string Yes Request content type:json or multipart
timeout integer Yes Request timeout in seconds (must be > 0)
success_code integer Yes Expected HTTP success code (100-599)
enabled boolean Yes Whether the API is enabled
response_type string No Expected response format
text_language string No Language code for text content (e.g.,en, uk)
socialify_image boolean Yes Whether to generate socialify images
default_json_body string No JSON string of default key/value pairs (supports {env.VAR})

Request Example:

{
  "name": "bluesky",
  "url": "{env.BLUESKY_URL}/bluesky/api/posts/create",
  "method": "POST",
  "auth_type": "api_key",
  "token_env_var": "BLUESKY_SERVER_KEY",
  "token_header": "X-API-Key",
  "content_type": "multipart",
  "timeout": 30,
  "success_code": 200,
  "enabled": true,
  "response_type": "json",
  "text_language": "en",
  "socialify_image": false,
  "default_json_body": ""
}

Response Example:

{
  "id": 3,
  "name": "bluesky",
  "url": "{env.BLUESKY_URL}/bluesky/api/posts/create",
  "method": "POST",
  "auth_type": "api_key",
  "token_env_var": "BLUESKY_SERVER_KEY",
  "token_header": "X-API-Key",
  "content_type": "multipart",
  "timeout": 30,
  "success_code": 200,
  "enabled": true,
  "response_type": "json",
  "text_language": "en",
  "socialify_image": false,
  "default_json_body": "",
  "updated_at": "2024-03-15T10:00:00Z"
}

/api/api-configs/ (update)

Endpoint: /api/api-configs/{name}

Method: PUT

Description: Update an existing API configuration. All fields are optional - only provide the fields you want to update.

Curl Example:

curl -X PUT \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": false,
    "timeout": 60
  }' \
  http://localhost:8080/api/api-configs/twitter

Request Parameters:

All fields are optional. Only include the fields you want to update.

Parameter Type Description
url string The endpoint URL (supports {env.VAR} syntax)
method string HTTP method (GET, POST, PUT, DELETE, PATCH)
auth_type string Authentication type:bearer, api_key, or empty
token_env_var string Environment variable name containing the auth token
token_header string Header name for API key
content_type string Request content type:json or multipart
timeout integer Request timeout in seconds (must be > 0)
success_code integer Expected HTTP success code (100-599)
enabled boolean Whether the API is enabled
response_type string Expected response format
text_language string Language code for text content (e.g.,en, uk)
socialify_image boolean Whether to generate socialify images
default_json_body string JSON string of default key/value pairs (supports {env.VAR})

Request Example:

{
  "enabled": false,
  "timeout": 60,
  "text_language": "es"
}

Response Example:

{
  "id": 1,
  "name": "twitter",
  "url": "{env.TWITTER_URL}/x/api/posts/create",
  "method": "POST",
  "auth_type": "api_key",
  "token_env_var": "TWITTER_API_KEY",
  "token_header": "X-API-Key",
  "content_type": "multipart",
  "timeout": 60,
  "success_code": 200,
  "enabled": false,
  "response_type": "json",
  "text_language": "es",
  "socialify_image": false,
  "default_json_body": "",
  "updated_at": "2024-03-15T11:00:00Z"
}

/api/api-configs/ (delete)

Endpoint: /api/api-configs/{name}

Method: DELETE

Description: Delete an API configuration.

Curl Example:

curl -X DELETE \
  -H "Authorization: Bearer <API_TOKEN>" \
  http://localhost:8080/api/api-configs/twitter

Response Example:

{
  "status": "success",
  "message": "API config deleted successfully"
}

API Configuration Notes:

  • Environment Variables: Use {env.VARIABLE_NAME} syntax in url and default_json_body fields to reference environment variables
  • Default JSON Body: For APIs with content_type: json, you can specify default key/value pairs that are always included in requests. Store as a JSON string, e.g., {"type": "chat", "jid": "{env.WAPP_JID}"}
  • Auto-Reload: After creating, updating, or deleting an API configuration, the system automatically reloads all configurations to apply changes immediately
  • Migration: On first startup (v3.4.0+), existing configurations from apis-config.yml are automatically migrated to the database

Status Codes:

  • 200: Success (GET, PUT)
  • 201: Created (POST)
  • 400: Bad Request - Invalid parameters or validation errors
  • 401: Unauthorized - Invalid or missing Bearer token
  • 404: Not Found - API configuration does not exist (GET, PUT, DELETE)
  • 500: Internal Server Error - Database or server error