{"openapi":"3.1.0","info":{"title":"AS213905 API","version":"1.2.0","description":"Customer automation API for organisations, invoices, services, traffic,\ntunnels and RFC 8805 geofeeds. Create a scoped key in Panel → Settings.\n","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/as213905-sdk/src/branch/main/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","description":"Returns the single organisation permanently bound to the bearer key. Requires `organizations:read`.","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","description":"Returns all billing documents for the key organisation, ordered from newest to oldest. Requires `invoices:read`.","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","description":"Returns every customer service and its lifecycle state. Requires `services:read`.","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","description":"Returns one service owned by the key organisation. A foreign identifier is reported as not found. Requires `services:read`.","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","description":"Reads a sampled metric series from a controllable BGP VM. Requires `services:read`.","operationId":"getServiceMetrics","x-required-scope":"services:read","parameters":[{"name":"target","in":"query","description":"Metric to read. Network values are returned as bytes per second; percentage and load values use their native units.","example":"net_tx","schema":{"type":"string","enum":["cpu_load","mem_usage_percent","net_rx","net_tx","iops","df.root.used"],"default":"cpu_load"}},{"name":"range","in":"query","description":"Time window ending at the current time.","example":"6h","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","description":"Queues an asynchronous power command for a controllable BGP VM. Requires `services:write`.","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"],"examples":["restart"],"description":"Power transition to queue."}}},"examples":{"restart":{"value":{"action":"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","description":"Returns transferred bytes and 95th-percentile traffic for either the last N days or an explicit UTC date interval. `days` is mutually exclusive with `from`/`to`. Requires `traffic:read`.","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","description":"Returns all transit tunnels and their provisioning state. Requires `tunnels:read`.","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","description":"Records a Frankfurt transit tunnel for asynchronous provisioning. Currently `fra` is the only available location. Requires `tunnels:write`.","operationId":"createTunnel","x-required-scope":"tunnels:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTunnelRequest"},"examples":{"frankfurt":{"summary":"Frankfurt GRE tunnel","value":{"peer_endpoint":"203.0.113.10","location":"fra","label":"fra-primary","announce":true}}}}}},"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","description":"Queues teardown and returns before router configuration has completed. Requires `tunnels:write`.","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","description":"Returns transferred bytes and 95th-percentile traffic for one tunnel. `days` is mutually exclusive with `from`/`to`. Requires `traffic:read`.","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","description":"Lists the organisation's public RFC 8805 feeds. Each organisation can own at most two. Requires `geofeeds:read`.","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)","description":"Creates an empty feed and an unguessable public CSV URL. Requires `geofeeds:write`.","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,"examples":["Customer prefixes"],"description":"Human-readable display name."}}},"examples":{"default":{"value":{"name":"Customer prefixes"}}}}}},"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","description":"Returns feed metadata and all current RFC 8805 records. Requires `geofeeds:read`.","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","description":"Changes only the display name; the public URL remains valid. Requires `geofeeds:write`.","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,"examples":["Production geofeed"],"description":"New display name."}}},"examples":{"rename":{"value":{"name":"Production geofeed"}}}}}},"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","description":"Permanently deletes the feed, its records and public CSV URL. Requires `geofeeds:write`.","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","description":"Validates the complete array and replaces all records in one transaction. No partial update is committed. Requires `geofeeds:write`.","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"}}}},"examples":{"frankfurt":{"value":{"records":[{"prefix":"203.0.113.0/24","country_code":"DE","region_code":"DE-HE","city":"Frankfurt am Main","postal_code":"60311"}]}}}}}},"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","description":"Invalidates the previous URL immediately and issues a new public slug. Requires `geofeeds:write`.","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,"description":"UUID returned by the corresponding list endpoint.","example":"38fce115-83e6-4122-a65a-9c70c9187794","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.","example":"9f44e0e7-13d4-4af3-bc6e-76b91c93b752","schema":{"type":"string","format":"uuid"}},"Days":{"name":"days","in":"query","description":"Last N days; mutually exclusive with from/to.","example":7,"schema":{"type":"integer","minimum":1,"maximum":366,"default":7}},"From":{"name":"from","in":"query","description":"First UTC calendar date in an explicit interval. Use together with `to`; mutually exclusive with `days`.","example":"2026-08-01","schema":{"type":"string","format":"date"}},"To":{"name":"to","in":"query","description":"Inclusive whole-day end.","example":"2026-08-07","schema":{"type":"string","format":"date"}}},"responses":{"BadRequest":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"invalid_parameter":{"value":{"error":"invalid_parameter","message":"days cannot be combined with from/to"}}}}}},"Unauthenticated":{"description":"Missing, malformed, expired, revoked or unknown API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"missing":{"value":{"error":"api_key_required","message":"Authorization header is required"}},"invalid":{"value":{"error":"api_key_invalid","message":"The API key is invalid or expired"}}}}}},"Forbidden":{"description":"Missing scope or disallowed source address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"scope":{"value":{"error":"scope_required","message":"This endpoint requires traffic:read","required_scope":"traffic:read"}},"address":{"value":{"error":"address_not_allowed","message":"The request source is not allowed"}}}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"resource":{"value":{"error":"not_found","message":"Resource not found"}}}}}},"Conflict":{"description":"Resource limit or state conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"limit":{"value":{"error":"geofeed_limit_reached","message":"An organisation can have at most two geofeeds"}}}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds before another attempt should be made.","schema":{"type":"integer","minimum":1,"examples":[30]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"},"examples":{"limited":{"value":{"error":"rate_limited","message":"Too many requests","retry_after":30}}}}}}},"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"}},"examples":[{"id":"9f44e0e7-13d4-4af3-bc6e-76b91c93b752","asn":213905,"name":"Example Networks GmbH","status":"active","commit_kbps":1000000}]},"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"}},"examples":[{"id":"c089452d-9127-48b6-a1b8-407f9691a658","number":"INV-2026-0042","status":"paid","currency":"EUR","total_cents":900,"period_from":"2026-08-01","period_to":"2026-08-31","issued_at":"2026-08-01T09:00:00Z","due_at":"2026-08-08T09:00:00Z"}]},"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}},"examples":[{"id":"38fce115-83e6-4122-a65a-9c70c9187794","kind":"bgp_vm","label":"BGP Small","status":"active","monthly_cents":900,"next_due_on":"2026-09-01","controllable":true,"location":"fra","configuration":{"vcpu":2,"memory_gib":4,"disk_gib":50}}]},"MetricPoint":{"type":"object","required":["t"],"properties":{"t":{"type":"string","format":"date-time"},"v":{"type":"number"}},"examples":[{"t":"2026-08-14T14:00:00Z","v":42.7}]},"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"}}},"examples":[{"metric":"Network transmit","target":"net_tx","range":"6h","points":[{"t":"2026-08-14T14:00:00Z","v":1250000},{"t":"2026-08-14T14:05:00Z","v":1310000}]}]},"Period":{"type":"object","required":["from","to","label"],"properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"label":{"type":"string"}},"examples":[{"from":"2026-08-01T00:00:00Z","to":"2026-08-08T00:00:00Z","label":"1–7 August 2026"}]},"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"}},"examples":[{"p95_kbps":428500,"commit_kbps":1000000,"over_commit":false,"total_bytes":482901204887,"total_gb":449.74,"period":{"from":"2026-08-01T00:00:00Z","to":"2026-08-08T00:00:00Z","label":"1–7 August 2026"}}]},"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"}},"examples":[{"id":"a5247bdd-e2d0-4c25-a1e6-c84e290cbfa1","status":"active","label":"fra-primary","location":"fra","peer_endpoint":"203.0.113.10","ifname":"gre-customer-1","inner_v4":"169.254.10.2/30","inner_v6":"fe80::2/64","announce":true}]},"CreateTunnelRequest":{"type":"object","required":["peer_endpoint","location"],"properties":{"peer_endpoint":{"type":"string","format":"ip","examples":["203.0.113.10"],"description":"Public remote GRE endpoint."},"location":{"type":"string","enum":["fra"],"examples":["fra"],"description":"Service location; currently Frankfurt only."},"label":{"type":"string","maxLength":100,"examples":["fra-primary"],"description":"Optional customer-visible name."},"announce":{"type":"boolean","default":false,"examples":[true],"description":"Request route announcement after configuration."}},"examples":[{"peer_endpoint":"203.0.113.10","location":"fra","label":"fra-primary","announce":true}]},"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"}},"examples":[{"id":"661e65fd-a498-4418-af2f-dbc8e4b24535","name":"Production geofeed","slug":"geo-Q4cW8sR1.csv","record_count":1,"created_at":"2026-08-14T10:00:00Z","updated_at":"2026-08-14T14:00:00Z"}]},"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,"examples":["60311"]}},"examples":[{"prefix":"203.0.113.0/24","country_code":"DE","region_code":"DE-HE","city":"Frankfurt am Main","postal_code":"60311"}]},"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}},"examples":[{"geofeeds":[{"id":"661e65fd-a498-4418-af2f-dbc8e4b24535","name":"Production geofeed","slug":"geo-Q4cW8sR1.csv","record_count":1,"created_at":"2026-08-14T10:00:00Z","updated_at":"2026-08-14T14:00:00Z"}],"limit":2}]}}}}