helpyourneighbour/openapi.yaml

687 lines
17 KiB
YAML
Raw Normal View History

openapi: 3.1.0
info:
title: HelpYourNeighbour API
version: 0.2.0
description: API contract for authentication, requests, offers, contacts, profile, addresses, and reviews.
servers:
- url: http://localhost:3000
description: Local development
security:
- bearerAuth: []
paths:
/health:
get:
tags: [system]
summary: Health check
security: []
responses:
'200':
description: Service healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthResponse'
/auth/register:
post:
tags: [auth]
summary: Register a new user
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/AuthTokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'409':
description: Email already exists
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Registration failed
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/login:
post:
tags: [auth]
summary: Login with email/password
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Login success
content:
application/json:
schema:
$ref: '#/components/schemas/AuthTokenResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
description: Invalid credentials
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/requests:
get:
tags: [requests]
summary: List help requests
security: []
responses:
'200':
description: Request list
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/HelpRequest'
post:
tags: [requests]
summary: Create help request
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateHelpRequest'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/IdResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/offers/{requestId}:
post:
tags: [offers]
summary: Create offer for help request
parameters:
- $ref: '#/components/parameters/RequestId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOfferRequest'
responses:
'201':
description: Offer created
content:
application/json:
schema:
$ref: '#/components/schemas/IdResponse'
'400':
$ref: '#/components/responses/BadRequestSimple'
'401':
$ref: '#/components/responses/Unauthorized'
/offers/negotiation/{offerId}:
post:
tags: [offers]
summary: Create counter-offer/negotiation message
parameters:
- $ref: '#/components/parameters/OfferId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOfferRequest'
responses:
'201':
description: Negotiation entry created
content:
application/json:
schema:
$ref: '#/components/schemas/IdResponse'
'400':
$ref: '#/components/responses/BadRequestSimple'
'401':
$ref: '#/components/responses/Unauthorized'
/offers/accept/{offerId}:
post:
tags: [offers]
summary: Accept offer and create deal
parameters:
- $ref: '#/components/parameters/OfferId'
responses:
'201':
description: Deal created
content:
application/json:
schema:
$ref: '#/components/schemas/DealCreatedResponse'
'400':
description: Invalid offer id
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
description: Offer not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/contacts/request:
post:
tags: [contacts]
summary: Request contact exchange for a deal
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ContactRequestCreate'
responses:
'201':
description: Request created
content:
application/json:
schema:
$ref: '#/components/schemas/IdResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Deal not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: Request already exists
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/contacts/respond:
post:
tags: [contacts]
summary: Accept or reject contact exchange request
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ContactRequestRespond'
responses:
'200':
description: Response processed
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Request not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/contacts/deal/{dealId}:
get:
tags: [contacts]
summary: List accepted contact exchanges for deal
parameters:
- $ref: '#/components/parameters/DealId'
responses:
'200':
description: Accepted contact exchange rows
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ContactExchangeRow'
'400':
description: Invalid dealId
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Deal not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/addresses/change-request:
post:
tags: [addresses]
summary: Request address change with postal verification code
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AddressChangeRequest'
responses:
'201':
description: Change request created
content:
application/json:
schema:
$ref: '#/components/schemas/AddressChangeCreatedResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/addresses/verify:
post:
tags: [addresses]
summary: Verify address change with 6-digit code
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AddressVerifyRequest'
responses:
'200':
description: Address verified
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'400':
description: Invalid payload or code
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Request not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: Request not pending
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/profile/phone:
post:
tags: [profile]
summary: Update encrypted phone number
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PhoneUpdateRequest'
responses:
'200':
description: Phone updated
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/reviews/{dealId}:
post:
tags: [reviews]
summary: Create review for deal
parameters:
- $ref: '#/components/parameters/DealId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateReviewRequest'
responses:
'201':
description: Review created
content:
application/json:
schema:
$ref: '#/components/schemas/IdResponse'
'400':
$ref: '#/components/responses/BadRequestSimple'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
parameters:
RequestId:
name: requestId
in: path
required: true
schema:
type: integer
minimum: 1
OfferId:
name: offerId
in: path
required: true
schema:
type: integer
minimum: 1
DealId:
name: dealId
in: path
required: true
schema:
type: integer
minimum: 1
responses:
BadRequest:
description: Validation failed
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
BadRequestSimple:
description: Invalid payload
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Unauthorized:
description: Missing or invalid token
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Forbidden:
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
schemas:
HealthResponse:
type: object
additionalProperties: false
properties:
status:
type: string
example: ok
required: [status]
ErrorResponse:
type: object
properties:
error:
oneOf:
- type: string
- type: object
StatusResponse:
type: object
additionalProperties: false
properties:
status:
type: string
required: [status]
IdResponse:
type: object
additionalProperties: false
properties:
id:
type: integer
minimum: 1
required: [id]
DealCreatedResponse:
type: object
additionalProperties: false
properties:
dealId:
type: integer
minimum: 1
required: [dealId]
AuthTokenResponse:
type: object
additionalProperties: false
properties:
token:
type: string
required: [token]
RegisterRequest:
type: object
additionalProperties: false
properties:
email:
type: string
format: email
password:
type: string
minLength: 8
displayName:
type: string
minLength: 2
maxLength: 120
required: [email, password, displayName]
LoginRequest:
type: object
additionalProperties: false
properties:
email:
type: string
format: email
password:
type: string
minLength: 1
required: [email, password]
HelpRequest:
type: object
properties:
id:
type: integer
title:
type: string
description:
type: string
value_chf:
type: number
status:
type: string
created_at:
type: string
format: date-time
requester_name:
type: string
CreateHelpRequest:
type: object
additionalProperties: false
properties:
title:
type: string
minLength: 3
maxLength: 180
description:
type: string
minLength: 5
valueChf:
type: number
exclusiveMinimum: 0
required: [title, description, valueChf]
CreateOfferRequest:
type: object
additionalProperties: false
properties:
amountChf:
type: number
exclusiveMinimum: 0
message:
type: string
maxLength: 2000
required: [amountChf]
ContactRequestCreate:
type: object
additionalProperties: false
properties:
dealId:
type: integer
minimum: 1
targetUserId:
type: integer
minimum: 1
required: [dealId, targetUserId]
ContactRequestRespond:
type: object
additionalProperties: false
properties:
requestId:
type: integer
minimum: 1
accept:
type: boolean
required: [requestId, accept]
ContactExchangeRow:
type: object
properties:
id:
type: integer
requester_id:
type: integer
target_id:
type: integer
accepted:
type: boolean
requester_phone_encrypted:
type: string
target_phone_encrypted:
type: string
AddressChangeRequest:
type: object
additionalProperties: false
properties:
newAddress:
type: string
minLength: 10
required: [newAddress]
AddressChangeCreatedResponse:
type: object
additionalProperties: false
properties:
requestId:
type: integer
minimum: 1
postalDispatch:
type: string
example: pending_letter
note:
type: string
verificationCode:
type: string
pattern: '^\\d{6}$'
required: [requestId, postalDispatch, note, verificationCode]
AddressVerifyRequest:
type: object
additionalProperties: false
properties:
requestId:
type: integer
minimum: 1
code:
type: string
pattern: '^\\d{6}$'
required: [requestId, code]
PhoneUpdateRequest:
type: object
additionalProperties: false
properties:
phone:
type: string
minLength: 6
maxLength: 40
required: [phone]
CreateReviewRequest:
type: object
additionalProperties: false
properties:
revieweeId:
type: integer
minimum: 1
rating:
type: integer
minimum: 1
maximum: 5
comment:
type: string
maxLength: 2000
required: [revieweeId, rating]