Files
as213905-sdk/openapi/openapi.yaml
T

612 lines
21 KiB
YAML

openapi: 3.1.0
info:
title: AS213905 API
version: 1.1.0
description: |
Customer automation API for organisations, invoices, services, traffic,
tunnels and RFC 8805 geofeeds. Create a scoped key in Panel → Settings.
contact:
name: AS213905 NOC
email: noc@as213905.com
url: https://as213905.com/docs/api
license:
name: Proprietary API; SDKs licensed under MIT
url: https://git.datacoria.com/Phylex/production-as213905/src/branch/recovery/native-geofeeds-20260814/as213905-sdk/LICENSE
servers:
- url: https://as213905.com
tags:
- name: Organisation
description: The organisation bound to the API key.
- name: Invoices
description: Billing documents and payment state.
- name: Services
description: Customer services, BGP VM metrics and power actions.
- name: Traffic
description: Organisation and per-tunnel traffic accounting.
- name: Tunnels
description: Transit tunnel inventory and lifecycle.
- name: Geofeeds
description: RFC 8805 public geofeed management.
security:
- apiKey: []
paths:
/api/v1/organization:
get:
tags: [Organisation]
summary: Get the API key's organisation
operationId: getOrganization
x-required-scope: organizations:read
responses:
'200':
description: Organisation
content:
application/json:
schema: {$ref: '#/components/schemas/OrganizationResponse'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
/api/v1/invoices:
get:
tags: [Invoices]
summary: List invoices, newest first
operationId: listInvoices
x-required-scope: invoices:read
responses:
'200':
description: Invoice list
content:
application/json:
schema:
type: object
required: [invoices]
properties:
invoices: {type: array, items: {$ref: '#/components/schemas/Invoice'}}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
/api/v1/services:
get:
tags: [Services]
summary: List services
operationId: listServices
x-required-scope: services:read
responses:
'200':
description: Service list
content:
application/json:
schema:
type: object
required: [services]
properties:
services: {type: array, items: {$ref: '#/components/schemas/Service'}}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
/api/v1/services/{id}:
parameters:
- {$ref: '#/components/parameters/ResourceId'}
get:
tags: [Services]
summary: Get one service
operationId: getService
x-required-scope: services:read
responses:
'200':
description: Service
content:
application/json:
schema:
type: object
required: [service]
properties:
service: {$ref: '#/components/schemas/Service'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/services/{id}/metrics:
parameters:
- {$ref: '#/components/parameters/ResourceId'}
get:
tags: [Services]
summary: Read a BGP VM metric series
operationId: getServiceMetrics
x-required-scope: services:read
parameters:
- name: target
in: query
schema:
type: string
enum: [cpu_load, mem_usage_percent, net_rx, net_tx, iops, df.root.used]
default: cpu_load
- name: range
in: query
schema: {type: string, enum: [1h, 6h, 24h, 7d], default: 24h}
responses:
'200':
description: Metric series
content:
application/json:
schema: {$ref: '#/components/schemas/MetricSeries'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/services/{id}/power:
parameters:
- {$ref: '#/components/parameters/ResourceId'}
post:
tags: [Services]
summary: Start, stop or restart a BGP VM
operationId: powerService
x-required-scope: services:write
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [action]
properties:
action: {type: string, enum: [start, stop, restart]}
responses:
'202':
description: Command accepted
content:
application/json:
schema:
type: object
required: [status, action]
properties:
status: {type: string, const: accepted}
action: {type: string, enum: [start, stop, restart]}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
'429': {$ref: '#/components/responses/RateLimited'}
/api/v1/traffic:
get:
tags: [Traffic]
summary: Get organisation transit usage
operationId: getTraffic
x-required-scope: traffic:read
parameters:
- {$ref: '#/components/parameters/Days'}
- {$ref: '#/components/parameters/From'}
- {$ref: '#/components/parameters/To'}
responses:
'200':
description: Traffic summary
content:
application/json:
schema: {$ref: '#/components/schemas/Traffic'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
/api/v1/tunnels:
get:
tags: [Tunnels]
summary: List tunnels
operationId: listTunnels
x-required-scope: tunnels:read
responses:
'200':
description: Tunnel list
content:
application/json:
schema:
type: object
required: [tunnels]
properties:
tunnels: {type: array, items: {$ref: '#/components/schemas/Tunnel'}}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
post:
tags: [Tunnels]
summary: Request a transit tunnel
operationId: createTunnel
x-required-scope: tunnels:write
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/CreateTunnelRequest'}
responses:
'202':
description: Tunnel recorded for provisioning
content:
application/json:
schema:
type: object
required: [tunnel]
properties:
tunnel: {$ref: '#/components/schemas/Tunnel'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
/api/v1/tunnels/{id}:
parameters:
- {$ref: '#/components/parameters/ResourceId'}
delete:
tags: [Tunnels]
summary: Remove a tunnel asynchronously
operationId: deleteTunnel
x-required-scope: tunnels:write
responses:
'202':
description: Removal accepted
content:
application/json:
schema: {$ref: '#/components/schemas/StatusResponse'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/tunnels/{id}/traffic:
parameters:
- {$ref: '#/components/parameters/ResourceId'}
get:
tags: [Traffic, Tunnels]
summary: Get one tunnel's usage
operationId: getTunnelTraffic
x-required-scope: traffic:read
parameters:
- {$ref: '#/components/parameters/Days'}
- {$ref: '#/components/parameters/From'}
- {$ref: '#/components/parameters/To'}
responses:
'200':
description: Tunnel traffic
content:
application/json:
schema: {$ref: '#/components/schemas/TunnelTraffic'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/geofeeds:
parameters:
- {$ref: '#/components/parameters/OrganizationId'}
get:
tags: [Geofeeds]
summary: List geofeeds
operationId: listGeofeeds
x-required-scope: geofeeds:read
responses:
'200':
description: Geofeed list
content:
application/json:
schema: {$ref: '#/components/schemas/GeofeedList'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
post:
tags: [Geofeeds]
summary: Create a geofeed (maximum two per organisation)
operationId: createGeofeed
x-required-scope: geofeeds:write
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: {type: string, minLength: 1, maxLength: 100}
responses:
'201':
description: Created
content:
application/json:
schema: {$ref: '#/components/schemas/GeofeedList'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'409': {$ref: '#/components/responses/Conflict'}
/api/v1/geofeeds/{id}:
parameters:
- {$ref: '#/components/parameters/OrganizationId'}
- {$ref: '#/components/parameters/ResourceId'}
get:
tags: [Geofeeds]
summary: Get a geofeed and its records
operationId: getGeofeed
x-required-scope: geofeeds:read
responses:
'200':
description: Geofeed
content:
application/json:
schema:
type: object
required: [geofeed]
properties:
geofeed: {$ref: '#/components/schemas/GeofeedWithRecords'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
patch:
tags: [Geofeeds]
summary: Rename a geofeed
operationId: updateGeofeed
x-required-scope: geofeeds:write
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: {type: string, minLength: 1, maxLength: 100}
responses:
'200':
description: Updated
content:
application/json:
schema: {$ref: '#/components/schemas/StatusResponse'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
delete:
tags: [Geofeeds]
summary: Delete a geofeed
operationId: deleteGeofeed
x-required-scope: geofeeds:write
responses:
'200':
description: Deleted
content:
application/json:
schema: {$ref: '#/components/schemas/StatusResponse'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/geofeeds/{id}/records:
parameters:
- {$ref: '#/components/parameters/OrganizationId'}
- {$ref: '#/components/parameters/ResourceId'}
put:
tags: [Geofeeds]
summary: Atomically replace all RFC 8805 records
operationId: replaceGeofeedRecords
x-required-scope: geofeeds:write
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [records]
properties:
records:
type: array
maxItems: 1000
items: {$ref: '#/components/schemas/GeofeedRecordInput'}
responses:
'200':
description: Saved
content:
application/json:
schema: {$ref: '#/components/schemas/StatusResponse'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/v1/geofeeds/{id}/rotate:
parameters:
- {$ref: '#/components/parameters/OrganizationId'}
- {$ref: '#/components/parameters/ResourceId'}
post:
tags: [Geofeeds]
summary: Rotate the unguessable public CSV URL
operationId: rotateGeofeedUrl
x-required-scope: geofeeds:write
responses:
'200':
description: Rotated
content:
application/json:
schema: {$ref: '#/components/schemas/GeofeedList'}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
components:
securitySchemes:
apiKey:
type: http
scheme: bearer
bearerFormat: as213905_…
description: API key from Panel → Settings. Query-string keys are rejected.
parameters:
ResourceId:
name: id
in: path
required: true
schema: {type: string, format: uuid}
OrganizationId:
name: X-Organization-ID
in: header
required: true
description: Organisation UUID. An API key can only address its own organisation.
schema: {type: string, format: uuid}
Days:
name: days
in: query
description: Last N days; mutually exclusive with from/to.
schema: {type: integer, minimum: 1, maximum: 366}
From:
name: from
in: query
schema: {type: string, format: date}
To:
name: to
in: query
description: Inclusive whole-day end.
schema: {type: string, format: date}
responses:
BadRequest:
description: Invalid request
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
Unauthenticated:
description: Missing, malformed, expired, revoked or unknown API key
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
Forbidden:
description: Missing scope or disallowed source address
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
NotFound:
description: Resource not found
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
Conflict:
description: Resource limit or state conflict
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
RateLimited:
description: Rate limit exceeded
headers:
Retry-After: {schema: {type: integer}}
content: {application/json: {schema: {$ref: '#/components/schemas/APIError'}}}
schemas:
APIError:
type: object
required: [error]
properties:
error: {type: string, examples: [scope_required]}
message: {type: string}
required_scope: {type: string}
retry_after: {type: integer}
StatusResponse:
type: object
required: [status]
properties: {status: {type: string}}
OrganizationResponse:
type: object
required: [organization]
properties: {organization: {$ref: '#/components/schemas/Organization'}}
Organization:
type: object
required: [id, asn, name, status, commit_kbps]
properties:
id: {type: string, format: uuid}
asn: {type: integer, minimum: 1, maximum: 4294967295}
name: {type: string}
status: {type: string}
commit_kbps: {type: integer, format: int64}
Invoice:
type: object
required: [id, number, status, currency, total_cents]
properties:
id: {type: string, format: uuid}
number: {type: string}
status: {type: string, enum: [draft, issued, paid, overdue, cancelled]}
currency: {type: string, minLength: 3, maxLength: 3}
total_cents: {type: integer, format: int64}
period_from: {type: [string, 'null'], format: date}
period_to: {type: [string, 'null'], format: date}
issued_at: {type: [string, 'null'], format: date-time}
due_at: {type: [string, 'null'], format: date-time}
Service:
type: object
required: [id, kind, label, status, monthly_cents]
properties:
id: {type: string, format: uuid}
kind: {type: string}
label: {type: string}
status: {type: string, enum: [awaiting_payment, provisioning, active, suspended, terminating, terminated]}
monthly_cents: {type: integer, format: int64}
next_due_on: {type: [string, 'null'], format: date}
controllable: {type: boolean}
location: {type: string}
configuration: {type: object, additionalProperties: true}
MetricPoint:
type: object
required: [t]
properties:
t: {type: string, format: date-time}
v: {type: number}
MetricSeries:
type: object
required: [metric, target, range, points]
properties:
metric: {type: string}
target: {type: string}
range: {type: string}
points: {type: array, items: {$ref: '#/components/schemas/MetricPoint'}}
Period:
type: object
required: [from, to, label]
properties:
from: {type: string, format: date-time}
to: {type: string, format: date-time}
label: {type: string}
Traffic:
type: object
required: [p95_kbps, total_bytes, total_gb, period]
properties:
p95_kbps: {type: integer, format: int64}
commit_kbps: {type: integer, format: int64}
over_commit: {type: boolean}
total_bytes: {type: integer, format: int64}
total_gb: {type: number}
period: {$ref: '#/components/schemas/Period'}
Tunnel:
type: object
required: [id, status, location, peer_endpoint]
properties:
id: {type: string, format: uuid}
status: {type: string, enum: [provisioning, configuring, awaiting_peer, active, error, suspended, removed]}
label: {type: string}
location: {type: string, examples: [fra]}
peer_endpoint: {type: string, format: ip}
ifname: {type: string}
inner_v4: {type: string}
inner_v6: {type: string}
announce: {type: boolean}
CreateTunnelRequest:
type: object
required: [peer_endpoint, location]
properties:
peer_endpoint: {type: string, format: ip}
location: {type: string, examples: [fra]}
label: {type: string, maxLength: 100}
announce: {type: boolean, default: false}
TunnelTraffic:
allOf:
- {$ref: '#/components/schemas/Traffic'}
- type: object
required: [tunnel]
properties: {tunnel: {$ref: '#/components/schemas/Tunnel'}}
Geofeed:
type: object
required: [id, name, slug, record_count, created_at, updated_at]
properties:
id: {type: string, format: uuid}
name: {type: string}
slug: {type: string}
record_count: {type: integer, minimum: 0}
created_at: {type: string, format: date-time}
updated_at: {type: string, format: date-time}
GeofeedRecordInput:
type: object
required: [prefix, country_code]
properties:
prefix: {type: string, examples: [203.0.113.0/24]}
country_code: {type: string, pattern: '^[A-Z]{2}$', examples: [DE]}
region_code: {type: string, maxLength: 128, examples: [DE-HE]}
city: {type: string, maxLength: 128, examples: [Frankfurt am Main]}
postal_code: {type: string, maxLength: 128}
GeofeedRecord:
allOf:
- {$ref: '#/components/schemas/GeofeedRecordInput'}
- type: object
properties: {id: {type: string, format: uuid}}
GeofeedWithRecords:
allOf:
- {$ref: '#/components/schemas/Geofeed'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/GeofeedRecord'}}
GeofeedList:
type: object
required: [geofeeds, limit]
properties:
geofeeds: {type: array, items: {$ref: '#/components/schemas/Geofeed'}}
limit: {type: integer, const: 2}