docs/error-api-openapi.yaml
Rama main · réplica de lectura del repositorio original.
openapi: 3.0.3
info:
title: Aptabase Error Reporting API
description: Error ingestion endpoint for the Aptabase error reporting feature.
version: 0.1.0
servers:
- url: https://localhost:3000
description: Local .NET backend (development)
paths:
/api/v0/error:
post:
operationId: postError
summary: Ingest an application error
description: |
Accepts an error report from an application SDK and buffers it for
storage in ClickHouse. The endpoint is CORS-enabled (AllowAny) and
rate-limited to 20 requests/second per IP (`EventIngestion` policy).
Returns **202 Accepted** on success (the error is buffered, not yet
persisted).
parameters:
- name: App-Key
in: header
required: true
description: |
The application API key. Identifies which app the error belongs to.
The key is normalised to uppercase on the server side.
schema:
type: string
example: "A-1234567890"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorBody"
example:
errorMessage: "NullReferenceException: Object reference not set to an instance of an object"
errorType: "NullReferenceException"
stackTrace: "at MyApp.Services.UserService.GetUser(Int32 id) in /src/Services/UserService.cs:line 42"
timestamp: "2026-02-17T12:00:00Z"
platform: "Android"
osName: "Android"
osVersion: "14"
appVersion: "1.2.3"
sdkVersion: "aptabase-flutter@0.1.0"
sessionId: "abc-123-def"
severity: "fatal"
kind: "crash"
isDebug: false
responses:
"202":
description: Accepted – error has been buffered for processing.
"400":
description: Bad request – invalid payload or missing App-Key header.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
invalidPayload:
value:
error: "Invalid error payload"
missingAppKey:
value:
error: "Missing App-Key header"
lockedAccount:
value:
error: "Owner account is locked"
"401":
description: Unauthorized – the App-Key does not match any registered app.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "Invalid App-Key"
"403":
description: |
Forbidden – the app has exhausted its monthly error quota.
Clients should NOT retry: the quota only resets on the 1st of
each month, so the report should be dropped. (403 is used
deliberately instead of 429, because SDKs treat 429 as
retryable.)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "Error quota exceeded"
"429":
description: |
Too Many Requests – returned ONLY by the per-IP rate limiter
(20 req/s per IP, `EventIngestion` policy). Retrying after a
short delay is appropriate. Quota exhaustion is reported as
403, never 429.
options:
operationId: optionsError
summary: CORS preflight for error ingestion
description: Returns 200 to satisfy browser CORS preflight checks.
responses:
"200":
description: OK
/api/v0/apps/{appId}/errors/types:
get:
operationId: getErrorTypes
summary: List top error types for an app
description: |
Returns the distinct error types recorded for an app within a date
range, together with how many times each occurred, ordered by
occurrence count (top 50). Used by the dashboard to populate the
error-type filter dropdown.
This is a dashboard endpoint: it requires an authenticated dashboard
session (cookie) and is rate-limited by the `Stats` policy
(1000 requests/hour per IP). Access is always checked against the
raw app id, before any build-mode suffix is applied.
parameters:
- name: appId
in: path
required: true
description: The app id (raw, without any build-mode suffix).
schema:
type: string
- name: buildMode
in: query
required: false
description: |
Which build of the app to query. `debug` reads errors stored
under the `_DEBUG`-suffixed app id; anything else (or omitted)
reads release errors.
schema:
type: string
enum: [release, debug]
- name: startDate
in: query
required: false
description: |
Start of the date range (ISO 8601). Defaults to 7 days before
`endDate` when omitted.
schema:
type: string
format: date-time
- name: endDate
in: query
required: false
description: |
End of the date range (ISO 8601). Defaults to the current time
when omitted.
schema:
type: string
format: date-time
responses:
"200":
description: The list of error types with occurrence counts.
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ErrorTypeItem"
example:
- name: "InvalidOperationException"
value: 42
- name: "NullReferenceException"
value: 17
"403":
description: Forbidden – the user does not have read access to the app.
components:
schemas:
ErrorBody:
type: object
required:
- errorMessage
- errorType
properties:
errorMessage:
type: string
maxLength: 5000
description: Human-readable error message.
errorType:
type: string
maxLength: 100
description: Error class/type name (e.g. "NullReferenceException", "TypeError").
stackTrace:
type: string
maxLength: 10000
description: Full stack trace. Optional.
timestamp:
type: string
format: date-time
description: |
When the error occurred (ISO 8601 / UTC). If omitted or in the
future, the server clamps it to the current time.
platform:
type: string
maxLength: 30
description: Platform identifier (e.g. "Android", "iOS", "Windows", "Web").
osName:
type: string
maxLength: 30
description: Operating system name.
osVersion:
type: string
maxLength: 100
description: Operating system version.
appVersion:
type: string
maxLength: 50
description: Application version string.
sdkVersion:
type: string
maxLength: 40
description: SDK identifier and version (e.g. "aptabase-flutter@0.1.0").
sessionId:
type: string
maxLength: 100
description: Client session identifier.
severity:
type: string
maxLength: 20
enum: [fatal, error]
description: |
Error severity. Matched case-insensitively against the known
values; unknown values are stored as an empty string (the report
itself is never rejected over an unknown severity).
kind:
type: string
maxLength: 20
enum: [crash, unhandled, taskException, handled]
description: |
How the error was captured. Matched case-insensitively against the
known values; unknown values are stored as an empty string (the
report itself is never rejected over an unknown kind).
isDebug:
type: boolean
default: false
description: |
Whether the error originated from a debug build of the app. Errors
from debug builds are stored separately from release builds (under
a `_DEBUG`-suffixed app id) so they never mix in the dashboard.
Older SDKs that omit this field are treated as release builds.
ErrorResponse:
type: object
properties:
error:
type: string
description: Human-readable error message.
ErrorTypeItem:
type: object
properties:
name:
type: string
description: The error type/class name (e.g. "InvalidOperationException").
value:
type: integer
description: Number of error events with this type within the date range.