176 lines
5.3 KiB
YAML
176 lines
5.3 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Sankofa HW Infra API
|
|
version: 0.1.0
|
|
servers:
|
|
- url: /api/v1
|
|
security:
|
|
- BearerAuth: []
|
|
components:
|
|
schemas:
|
|
ApiError:
|
|
type: object
|
|
properties:
|
|
error: { type: string, description: Human-readable message }
|
|
code: { type: string, enum: [BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, INTERNAL_ERROR] }
|
|
details: { type: object, description: Optional validation or extra data }
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
description: JWT with optional vendorId for vendor users
|
|
IngestionApiKey:
|
|
type: apiKey
|
|
in: header
|
|
name: x-ingestion-api-key
|
|
description: Required for POST /ingestion/offers (env INGESTION_API_KEY)
|
|
paths:
|
|
/health:
|
|
get:
|
|
summary: Health
|
|
security: []
|
|
/auth/token:
|
|
post:
|
|
summary: Get JWT token
|
|
description: Exchange email (and optional password) for a JWT with roles and vendorId. No auth required.
|
|
security: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [email]
|
|
properties:
|
|
email: { type: string, format: email }
|
|
password: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Token and user info
|
|
"401":
|
|
description: Invalid credentials
|
|
/vendors:
|
|
get:
|
|
summary: List vendors
|
|
description: If JWT contains vendorId (vendor user), returns only that vendor.
|
|
post:
|
|
summary: Create vendor
|
|
description: Forbidden for vendor users.
|
|
/vendors/{id}:
|
|
get:
|
|
summary: Get vendor
|
|
description: Vendor users may only request their own vendor id.
|
|
/offers:
|
|
get:
|
|
summary: List offers
|
|
description: If JWT contains vendorId, returns only that vendor's offers.
|
|
post:
|
|
summary: Create offer
|
|
description: Vendor users' vendorId is forced to their vendor.
|
|
/offers/{id}:
|
|
get:
|
|
summary: Get offer
|
|
patch:
|
|
summary: Update offer
|
|
delete:
|
|
summary: Delete offer
|
|
/purchase-orders:
|
|
get:
|
|
summary: List purchase orders
|
|
description: If JWT contains vendorId, returns only POs for that vendor.
|
|
/purchase-orders/{id}:
|
|
get:
|
|
summary: Get purchase order
|
|
/ingestion/offers:
|
|
post:
|
|
summary: Ingest offer (scrape or email)
|
|
description: Creates an offer with source (scraped|email), source_ref, source_metadata. Secured by x-ingestion-api-key only; no JWT. Use x-org-id for target org.
|
|
security:
|
|
- IngestionApiKey: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [source, quantity, unit_price]
|
|
properties:
|
|
source:
|
|
type: string
|
|
enum: [scraped, email]
|
|
source_ref:
|
|
type: string
|
|
description: URL or email message id
|
|
source_metadata:
|
|
type: object
|
|
vendor_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
sku:
|
|
type: string
|
|
mpn:
|
|
type: string
|
|
quantity:
|
|
type: integer
|
|
unit_price:
|
|
type: string
|
|
incoterms:
|
|
type: string
|
|
lead_time_days:
|
|
type: integer
|
|
country_of_origin:
|
|
type: string
|
|
condition:
|
|
type: string
|
|
warranty:
|
|
type: string
|
|
evidence_refs:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
key: { type: string }
|
|
hash: { type: string }
|
|
responses:
|
|
"201":
|
|
description: Offer created
|
|
"401":
|
|
description: Invalid or missing x-ingestion-api-key
|
|
/capacity/sites/{siteId}:
|
|
get:
|
|
summary: RU utilization for a site
|
|
description: Returns usedRu, totalRu, utilizationPercent for the site (from racks and assigned positions).
|
|
parameters:
|
|
- name: siteId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
"200":
|
|
description: Site capacity (usedRu, totalRu, utilizationPercent)
|
|
"404":
|
|
description: Site not found
|
|
/capacity/sites/{siteId}/power:
|
|
get:
|
|
summary: Power headroom for a site
|
|
description: Returns circuitLimitWatts from rack power_feeds; measuredDrawWatts/headroomWatts null until Phase 4.
|
|
parameters:
|
|
- name: siteId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
"200":
|
|
description: Power info (circuitLimitWatts, measuredDrawWatts, headroomWatts)
|
|
"404":
|
|
description: Site not found
|
|
/capacity/gpu-inventory:
|
|
get:
|
|
summary: GPU inventory
|
|
description: Returns total, bySite, and byType (part number) counts.
|
|
responses:
|
|
"200":
|
|
description: GPU counts (total, bySite, byType)
|