mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 22:34:47 +00:00
Add RSwag coverage for /chat and /transactions API endpoints (#210)
* Add RSwag coverage for chat API * Linter Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> * Add transaction rswag * FIX linter --------- Signed-off-by: Juan José Mata <jjmata@jjmata.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: sokie <sokysrm@gmail.com>
This commit is contained in:
@@ -1,228 +1,59 @@
|
||||
# Chat API Documentation
|
||||
|
||||
The Chat API allows external applications to interact with Sure's AI chat functionality.
|
||||
The Chat API allows external applications to interact with Sure's AI chat functionality. The OpenAPI description is generated directly from executable request specs, ensuring it always reflects the behaviour of the running Rails application.
|
||||
|
||||
## Authentication
|
||||
## Generated OpenAPI specification
|
||||
|
||||
All chat endpoints require authentication via OAuth2 or API keys. The chat endpoints also require the user to have AI features enabled (`ai_enabled: true`).
|
||||
- The source of truth for the documentation lives in [`spec/requests/api/v1/chats_spec.rb`](../../spec/requests/api/v1/chats_spec.rb). These specs authenticate against the Rails stack, exercise every chat endpoint, and capture real response shapes.
|
||||
- Regenerate the OpenAPI document with:
|
||||
|
||||
## Endpoints
|
||||
```sh
|
||||
RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
|
||||
```
|
||||
|
||||
### List Chats
|
||||
```
|
||||
GET /api/v1/chats
|
||||
```
|
||||
The task compiles the request specs and writes the result to [`docs/api/openapi.yaml`](openapi.yaml).
|
||||
|
||||
**Required Scope:** `read`
|
||||
- Run just the documentation specs with:
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"chats": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"title": "Chat title",
|
||||
"last_message_at": "2024-01-01T00:00:00Z",
|
||||
"message_count": 5,
|
||||
"error": null,
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total_count": 50,
|
||||
"total_pages": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
```sh
|
||||
bundle exec rspec spec/requests/api/v1/chats_spec.rb
|
||||
```
|
||||
|
||||
### Get Chat
|
||||
```
|
||||
GET /api/v1/chats/:id
|
||||
```
|
||||
## Authentication requirements
|
||||
|
||||
**Required Scope:** `read`
|
||||
All chat endpoints require an OAuth2 access token or API key that grants the appropriate scope. The authenticated user must also have AI features enabled (`ai_enabled: true`).
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"title": "Chat title",
|
||||
"error": null,
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z",
|
||||
"messages": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"type": "user_message",
|
||||
"role": "user",
|
||||
"content": "Hello AI",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "uuid",
|
||||
"type": "assistant_message",
|
||||
"role": "assistant",
|
||||
"content": "Hello! How can I help you?",
|
||||
"model": "gpt-4",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z",
|
||||
"tool_calls": []
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 50,
|
||||
"total_count": 2,
|
||||
"total_pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
## Available endpoints
|
||||
|
||||
### Create Chat
|
||||
```
|
||||
POST /api/v1/chats
|
||||
```
|
||||
| Endpoint | Scope | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET /api/v1/chats` | `read` | List chats for the authenticated user with pagination metadata. |
|
||||
| `GET /api/v1/chats/{id}` | `read` | Retrieve a chat, including ordered messages and optional pagination. |
|
||||
| `POST /api/v1/chats` | `write` | Create a chat and optionally seed it with an initial user message. |
|
||||
| `PATCH /api/v1/chats/{id}` | `write` | Update a chat title. |
|
||||
| `DELETE /api/v1/chats/{id}` | `write` | Permanently delete a chat. |
|
||||
| `POST /api/v1/chats/{chat_id}/messages` | `write` | Append a user message to a chat. |
|
||||
| `POST /api/v1/chats/{chat_id}/messages/retry` | `write` | Retry the last assistant response in a chat. |
|
||||
|
||||
**Required Scope:** `write`
|
||||
Refer to the generated [`openapi.yaml`](openapi.yaml) for request/response schemas, reusable components (pagination, errors, messages, tool calls), and security definitions.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"title": "Optional chat title",
|
||||
"message": "Initial message to AI",
|
||||
"model": "gpt-4" // optional, defaults to gpt-4
|
||||
}
|
||||
```
|
||||
## AI response behaviour
|
||||
|
||||
**Response:** Same as Get Chat endpoint
|
||||
- Chat creation and message submission queue AI processing jobs asynchronously; the API responds immediately with the user message payload.
|
||||
- Poll `GET /api/v1/chats/{id}` to detect new assistant messages (`type: "assistant_message"`).
|
||||
- Supported models today: `gpt-4` (default), `gpt-4-turbo`, and `gpt-3.5-turbo`.
|
||||
- Assistant responses may include structured tool calls (`tool_calls`) that reference financial data fetches and their results.
|
||||
|
||||
### Update Chat
|
||||
```
|
||||
PATCH /api/v1/chats/:id
|
||||
```
|
||||
## Error responses
|
||||
|
||||
**Required Scope:** `write`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"title": "New chat title"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** Same as Get Chat endpoint
|
||||
|
||||
### Delete Chat
|
||||
```
|
||||
DELETE /api/v1/chats/:id
|
||||
```
|
||||
|
||||
**Required Scope:** `write`
|
||||
|
||||
**Response:** 204 No Content
|
||||
|
||||
### Create Message
|
||||
```
|
||||
POST /api/v1/chats/:chat_id/messages
|
||||
```
|
||||
|
||||
**Required Scope:** `write`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"content": "User message",
|
||||
"model": "gpt-4" // optional, defaults to gpt-4
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"chat_id": "uuid",
|
||||
"type": "user_message",
|
||||
"role": "user",
|
||||
"content": "User message",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z",
|
||||
"ai_response_status": "pending",
|
||||
"ai_response_message": "AI response is being generated"
|
||||
}
|
||||
```
|
||||
|
||||
### Retry Last Message
|
||||
```
|
||||
POST /api/v1/chats/:chat_id/messages/retry
|
||||
```
|
||||
|
||||
**Required Scope:** `write`
|
||||
|
||||
Retries the last assistant message in the chat.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "Retry initiated",
|
||||
"message_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## AI Response Handling
|
||||
|
||||
AI responses are processed asynchronously. When you create a message or chat with an initial message, the API returns immediately with the user message. The AI response is generated in the background.
|
||||
|
||||
### Checking for AI Responses
|
||||
|
||||
Currently, you need to poll the chat endpoint to check for new AI responses. Look for new messages with `type: "assistant_message"`.
|
||||
|
||||
### Available AI Models
|
||||
|
||||
- `gpt-4` (default)
|
||||
- `gpt-4-turbo`
|
||||
- `gpt-3.5-turbo`
|
||||
|
||||
### Tool Calls
|
||||
|
||||
The AI assistant can make tool calls to access user financial data. These appear in the `tool_calls` array of assistant messages:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"function_name": "get_accounts",
|
||||
"function_arguments": {},
|
||||
"function_result": { ... },
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All endpoints return standard error responses:
|
||||
Errors conform to the shared `ErrorResponse` schema in the OpenAPI document:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "error_code",
|
||||
"message": "Human readable error message",
|
||||
"details": ["Additional error details"] // optional
|
||||
"details": ["Optional array of extra context"]
|
||||
}
|
||||
```
|
||||
|
||||
Common error codes:
|
||||
- `unauthorized` - Invalid or missing authentication
|
||||
- `forbidden` - Insufficient permissions or AI not enabled
|
||||
- `not_found` - Resource not found
|
||||
- `unprocessable_entity` - Invalid request data
|
||||
- `rate_limit_exceeded` - Too many requests
|
||||
|
||||
## Rate Limits
|
||||
|
||||
Chat API endpoints are subject to the standard API rate limits based on your API key tier.
|
||||
Common error codes include `unauthorized`, `forbidden`, `feature_disabled`, `not_found`, `unprocessable_entity`, and `rate_limit_exceeded`.
|
||||
888
docs/api/openapi.yaml
Normal file
888
docs/api/openapi.yaml
Normal file
@@ -0,0 +1,888 @@
|
||||
---
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Sure API
|
||||
version: v1
|
||||
description: OpenAPI documentation generated from executable request specs.
|
||||
servers:
|
||||
- url: https://api.sure.app
|
||||
description: Production
|
||||
- url: http://localhost:3000
|
||||
description: Local development
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
schemas:
|
||||
Pagination:
|
||||
type: object
|
||||
required:
|
||||
- page
|
||||
- per_page
|
||||
- total_count
|
||||
- total_pages
|
||||
properties:
|
||||
page:
|
||||
type: integer
|
||||
minimum: 1
|
||||
per_page:
|
||||
type: integer
|
||||
minimum: 1
|
||||
total_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
total_pages:
|
||||
type: integer
|
||||
minimum: 0
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
nullable: true
|
||||
details:
|
||||
oneOf:
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
- type: object
|
||||
nullable: true
|
||||
ToolCall:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- function_name
|
||||
- function_arguments
|
||||
- created_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
function_name:
|
||||
type: string
|
||||
function_arguments:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
function_result:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
nullable: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
Message:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- type
|
||||
- role
|
||||
- content
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- user_message
|
||||
- assistant_message
|
||||
role:
|
||||
type: string
|
||||
enum:
|
||||
- user
|
||||
- assistant
|
||||
content:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
nullable: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
tool_calls:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/ToolCall"
|
||||
nullable: true
|
||||
MessageResponse:
|
||||
allOf:
|
||||
- "$ref": "#/components/schemas/Message"
|
||||
- type: object
|
||||
required:
|
||||
- chat_id
|
||||
properties:
|
||||
chat_id:
|
||||
type: string
|
||||
format: uuid
|
||||
ai_response_status:
|
||||
type: string
|
||||
enum:
|
||||
- pending
|
||||
- complete
|
||||
- failed
|
||||
nullable: true
|
||||
ai_response_message:
|
||||
type: string
|
||||
nullable: true
|
||||
ChatResource:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- title
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
title:
|
||||
type: string
|
||||
error:
|
||||
type: string
|
||||
nullable: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
ChatSummary:
|
||||
allOf:
|
||||
- "$ref": "#/components/schemas/ChatResource"
|
||||
- type: object
|
||||
required:
|
||||
- message_count
|
||||
properties:
|
||||
message_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
last_message_at:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
ChatDetail:
|
||||
allOf:
|
||||
- "$ref": "#/components/schemas/ChatResource"
|
||||
- type: object
|
||||
required:
|
||||
- messages
|
||||
properties:
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Message"
|
||||
pagination:
|
||||
"$ref": "#/components/schemas/Pagination"
|
||||
nullable: true
|
||||
ChatCollection:
|
||||
type: object
|
||||
required:
|
||||
- chats
|
||||
- pagination
|
||||
properties:
|
||||
chats:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/ChatSummary"
|
||||
pagination:
|
||||
"$ref": "#/components/schemas/Pagination"
|
||||
RetryResponse:
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
- message_id
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
message_id:
|
||||
type: string
|
||||
format: uuid
|
||||
Account:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- account_type
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
account_type:
|
||||
type: string
|
||||
Category:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- classification
|
||||
- color
|
||||
- icon
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
classification:
|
||||
type: string
|
||||
color:
|
||||
type: string
|
||||
icon:
|
||||
type: string
|
||||
Merchant:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
Tag:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- color
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
color:
|
||||
type: string
|
||||
Transfer:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- amount
|
||||
- currency
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
amount:
|
||||
type: string
|
||||
currency:
|
||||
type: string
|
||||
other_account:
|
||||
"$ref": "#/components/schemas/Account"
|
||||
nullable: true
|
||||
Transaction:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- date
|
||||
- amount
|
||||
- currency
|
||||
- name
|
||||
- classification
|
||||
- account
|
||||
- tags
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
date:
|
||||
type: string
|
||||
format: date
|
||||
amount:
|
||||
type: string
|
||||
currency:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
notes:
|
||||
type: string
|
||||
nullable: true
|
||||
classification:
|
||||
type: string
|
||||
account:
|
||||
"$ref": "#/components/schemas/Account"
|
||||
category:
|
||||
"$ref": "#/components/schemas/Category"
|
||||
nullable: true
|
||||
merchant:
|
||||
"$ref": "#/components/schemas/Merchant"
|
||||
nullable: true
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Tag"
|
||||
transfer:
|
||||
"$ref": "#/components/schemas/Transfer"
|
||||
nullable: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
TransactionCollection:
|
||||
type: object
|
||||
required:
|
||||
- transactions
|
||||
- pagination
|
||||
properties:
|
||||
transactions:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Transaction"
|
||||
pagination:
|
||||
"$ref": "#/components/schemas/Pagination"
|
||||
DeleteResponse:
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
paths:
|
||||
"/api/v1/chats":
|
||||
get:
|
||||
summary: List chats
|
||||
tags:
|
||||
- Chats
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with read scope
|
||||
responses:
|
||||
'200':
|
||||
description: chats listed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ChatCollection"
|
||||
'403':
|
||||
description: AI features disabled
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
post:
|
||||
summary: Create chat
|
||||
tags:
|
||||
- Chats
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with write scope
|
||||
responses:
|
||||
'201':
|
||||
description: chat created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ChatDetail"
|
||||
'422':
|
||||
description: validation error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
example: Monthly budget review
|
||||
message:
|
||||
type: string
|
||||
description: Initial message in the chat
|
||||
model:
|
||||
type: string
|
||||
description: Optional OpenAI model identifier
|
||||
required:
|
||||
- title
|
||||
- message
|
||||
required: true
|
||||
"/api/v1/chats/{id}":
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with read scope
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Chat ID
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
summary: Retrieve a chat
|
||||
tags:
|
||||
- Chats
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: chat retrieved
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ChatDetail"
|
||||
'404':
|
||||
description: chat not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
patch:
|
||||
summary: Update a chat
|
||||
tags:
|
||||
- Chats
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: chat updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ChatDetail"
|
||||
'404':
|
||||
description: chat not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
'422':
|
||||
description: validation error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
example: Updated chat title
|
||||
required: true
|
||||
delete:
|
||||
summary: Delete a chat
|
||||
tags:
|
||||
- Chats
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'204':
|
||||
description: chat deleted
|
||||
'404':
|
||||
description: chat not found
|
||||
"/api/v1/chats/{chat_id}/messages":
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with write scope
|
||||
- name: chat_id
|
||||
in: path
|
||||
required: true
|
||||
description: Chat ID
|
||||
schema:
|
||||
type: string
|
||||
post:
|
||||
summary: Create a message
|
||||
tags:
|
||||
- Chat Messages
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters: []
|
||||
responses:
|
||||
'201':
|
||||
description: message created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/MessageResponse"
|
||||
'404':
|
||||
description: chat not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
'422':
|
||||
description: validation error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
required: true
|
||||
"/api/v1/chats/{chat_id}/messages/retry":
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with write scope
|
||||
- name: chat_id
|
||||
in: path
|
||||
required: true
|
||||
description: Chat ID
|
||||
schema:
|
||||
type: string
|
||||
post:
|
||||
summary: Retry the last assistant response
|
||||
tags:
|
||||
- Chat Messages
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'202':
|
||||
description: retry started
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/RetryResponse"
|
||||
'404':
|
||||
description: chat not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
'422':
|
||||
description: no assistant message available
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
"/api/v1/transactions":
|
||||
get:
|
||||
summary: List transactions
|
||||
tags:
|
||||
- Transactions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with read scope
|
||||
- name: page
|
||||
in: query
|
||||
required: false
|
||||
description: 'Page number (default: 1)'
|
||||
schema:
|
||||
type: integer
|
||||
- name: per_page
|
||||
in: query
|
||||
required: false
|
||||
description: 'Items per page (default: 25, max: 100)'
|
||||
schema:
|
||||
type: integer
|
||||
- name: account_id
|
||||
in: query
|
||||
required: false
|
||||
description: Filter by account ID
|
||||
schema:
|
||||
type: string
|
||||
- name: category_id
|
||||
in: query
|
||||
required: false
|
||||
description: Filter by category ID
|
||||
schema:
|
||||
type: string
|
||||
- name: merchant_id
|
||||
in: query
|
||||
required: false
|
||||
description: Filter by merchant ID
|
||||
schema:
|
||||
type: string
|
||||
- name: start_date
|
||||
in: query
|
||||
format: date
|
||||
required: false
|
||||
description: Filter transactions from this date
|
||||
schema:
|
||||
type: string
|
||||
- name: end_date
|
||||
in: query
|
||||
format: date
|
||||
required: false
|
||||
description: Filter transactions until this date
|
||||
schema:
|
||||
type: string
|
||||
- name: min_amount
|
||||
in: query
|
||||
required: false
|
||||
description: Filter by minimum amount
|
||||
schema:
|
||||
type: number
|
||||
- name: max_amount
|
||||
in: query
|
||||
required: false
|
||||
description: Filter by maximum amount
|
||||
schema:
|
||||
type: number
|
||||
- name: type
|
||||
in: query
|
||||
enum:
|
||||
- income
|
||||
- expense
|
||||
required: false
|
||||
description: "Filter by transaction type:\n * `income` \n * `expense` \n "
|
||||
schema:
|
||||
type: string
|
||||
- name: search
|
||||
in: query
|
||||
required: false
|
||||
description: Search by name, notes, or merchant name
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: transactions filtered by date range
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/TransactionCollection"
|
||||
post:
|
||||
summary: Create transaction
|
||||
tags:
|
||||
- Transactions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token with write scope
|
||||
responses:
|
||||
'201':
|
||||
description: transaction created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Transaction"
|
||||
'422':
|
||||
description: validation error - missing required fields
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
transaction:
|
||||
type: object
|
||||
properties:
|
||||
account_id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Account ID (required)
|
||||
date:
|
||||
type: string
|
||||
format: date
|
||||
description: Transaction date
|
||||
amount:
|
||||
type: number
|
||||
description: Transaction amount
|
||||
name:
|
||||
type: string
|
||||
description: Transaction name/description
|
||||
notes:
|
||||
type: string
|
||||
description: Additional notes
|
||||
currency:
|
||||
type: string
|
||||
description: Currency code (defaults to family currency)
|
||||
category_id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Category ID
|
||||
merchant_id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Merchant ID
|
||||
nature:
|
||||
type: string
|
||||
enum:
|
||||
- income
|
||||
- expense
|
||||
- inflow
|
||||
- outflow
|
||||
description: Transaction nature (determines sign)
|
||||
tag_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Array of tag IDs
|
||||
required:
|
||||
- account_id
|
||||
- date
|
||||
- amount
|
||||
- name
|
||||
required:
|
||||
- transaction
|
||||
required: true
|
||||
"/api/v1/transactions/{id}":
|
||||
parameters:
|
||||
- name: Authorization
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Bearer token
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Transaction ID
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
summary: Retrieve a transaction
|
||||
tags:
|
||||
- Transactions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: transaction retrieved
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Transaction"
|
||||
'404':
|
||||
description: transaction not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
patch:
|
||||
summary: Update a transaction
|
||||
tags:
|
||||
- Transactions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: transaction updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Transaction"
|
||||
'404':
|
||||
description: transaction not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
transaction:
|
||||
type: object
|
||||
properties:
|
||||
date:
|
||||
type: string
|
||||
format: date
|
||||
amount:
|
||||
type: number
|
||||
name:
|
||||
type: string
|
||||
notes:
|
||||
type: string
|
||||
category_id:
|
||||
type: string
|
||||
format: uuid
|
||||
merchant_id:
|
||||
type: string
|
||||
format: uuid
|
||||
nature:
|
||||
type: string
|
||||
enum:
|
||||
- income
|
||||
- expense
|
||||
- inflow
|
||||
- outflow
|
||||
tag_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
delete:
|
||||
summary: Delete a transaction
|
||||
tags:
|
||||
- Transactions
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: transaction deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/DeleteResponse"
|
||||
'404':
|
||||
description: transaction not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
159
docs/api/transactions.md
Normal file
159
docs/api/transactions.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Transactions API Documentation
|
||||
|
||||
The Transactions API allows external applications to manage financial transactions within Sure. The OpenAPI description is generated directly from executable request specs, ensuring it always reflects the behaviour of the running Rails application.
|
||||
|
||||
## Generated OpenAPI specification
|
||||
|
||||
- The source of truth for the documentation lives in [`spec/requests/api/v1/transactions_spec.rb`](../../spec/requests/api/v1/transactions_spec.rb). These specs authenticate against the Rails stack, exercise every transaction endpoint, and capture real response shapes.
|
||||
- Regenerate the OpenAPI document with:
|
||||
|
||||
```sh
|
||||
SWAGGER_DRY_RUN=0 bundle exec rspec spec/requests --format Rswag::Specs::SwaggerFormatter
|
||||
```
|
||||
|
||||
The task compiles the request specs and writes the result to [`docs/api/openapi.yaml`](openapi.yaml).
|
||||
|
||||
- Run just the documentation specs with:
|
||||
|
||||
```sh
|
||||
bundle exec rspec spec/requests/api/v1/transactions_spec.rb
|
||||
```
|
||||
|
||||
## Authentication requirements
|
||||
|
||||
All transaction endpoints require an OAuth2 access token or API key that grants the appropriate scope (`read` or `read_write`).
|
||||
|
||||
## Available endpoints
|
||||
|
||||
| Endpoint | Scope | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET /api/v1/transactions` | `read` | List transactions with filtering and pagination. |
|
||||
| `GET /api/v1/transactions/{id}` | `read` | Retrieve a single transaction with full details. |
|
||||
| `POST /api/v1/transactions` | `write` | Create a new transaction. |
|
||||
| `PATCH /api/v1/transactions/{id}` | `write` | Update an existing transaction. |
|
||||
| `DELETE /api/v1/transactions/{id}` | `write` | Permanently delete a transaction. |
|
||||
|
||||
Refer to the generated [`openapi.yaml`](openapi.yaml) for request/response schemas, reusable components (pagination, errors, accounts, categories, merchants, tags), and security definitions.
|
||||
|
||||
## Filtering options
|
||||
|
||||
The `GET /api/v1/transactions` endpoint supports the following query parameters for filtering:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `page` | integer | Page number (default: 1) |
|
||||
| `per_page` | integer | Items per page (default: 25, max: 100) |
|
||||
| `account_id` | uuid | Filter by a single account ID |
|
||||
| `account_ids[]` | uuid[] | Filter by multiple account IDs |
|
||||
| `category_id` | uuid | Filter by a single category ID |
|
||||
| `category_ids[]` | uuid[] | Filter by multiple category IDs |
|
||||
| `merchant_id` | uuid | Filter by a single merchant ID |
|
||||
| `merchant_ids[]` | uuid[] | Filter by multiple merchant IDs |
|
||||
| `tag_ids[]` | uuid[] | Filter by tag IDs |
|
||||
| `start_date` | date | Filter transactions from this date (inclusive) |
|
||||
| `end_date` | date | Filter transactions until this date (inclusive) |
|
||||
| `min_amount` | number | Filter by minimum amount |
|
||||
| `max_amount` | number | Filter by maximum amount |
|
||||
| `type` | string | Filter by transaction type: `income` or `expense` |
|
||||
| `search` | string | Search by name, notes, or merchant name |
|
||||
|
||||
## Transaction object
|
||||
|
||||
A transaction response includes:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"date": "2024-01-15",
|
||||
"amount": "$75.50",
|
||||
"currency": "USD",
|
||||
"name": "Grocery shopping",
|
||||
"notes": "Weekly groceries",
|
||||
"classification": "expense",
|
||||
"account": {
|
||||
"id": "uuid",
|
||||
"name": "Checking Account",
|
||||
"account_type": "depository"
|
||||
},
|
||||
"category": {
|
||||
"id": "uuid",
|
||||
"name": "Groceries",
|
||||
"classification": "expense",
|
||||
"color": "#4CAF50",
|
||||
"icon": "shopping-cart"
|
||||
},
|
||||
"merchant": {
|
||||
"id": "uuid",
|
||||
"name": "Whole Foods"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"name": "Essential",
|
||||
"color": "#2196F3"
|
||||
}
|
||||
],
|
||||
"transfer": null,
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"updated_at": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Creating transactions
|
||||
|
||||
When creating a transaction, the `nature` field determines how the amount is stored:
|
||||
|
||||
| Nature | Behaviour |
|
||||
| --- | --- |
|
||||
| `income` / `inflow` | Amount is stored as negative (credit) |
|
||||
| `expense` / `outflow` | Amount is stored as positive (debit) |
|
||||
|
||||
Example request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"transaction": {
|
||||
"account_id": "uuid",
|
||||
"date": "2024-01-15",
|
||||
"amount": 75.50,
|
||||
"name": "Grocery shopping",
|
||||
"nature": "expense",
|
||||
"category_id": "uuid",
|
||||
"merchant_id": "uuid",
|
||||
"tag_ids": ["uuid", "uuid"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transfer transactions
|
||||
|
||||
If a transaction is part of a transfer between accounts, the `transfer` field will be populated with details about the linked transaction:
|
||||
|
||||
```json
|
||||
{
|
||||
"transfer": {
|
||||
"id": "uuid",
|
||||
"amount": "$500.00",
|
||||
"currency": "USD",
|
||||
"other_account": {
|
||||
"id": "uuid",
|
||||
"name": "Savings Account",
|
||||
"account_type": "depository"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error responses
|
||||
|
||||
Errors conform to the shared `ErrorResponse` schema in the OpenAPI document:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "error_code",
|
||||
"message": "Human readable error message",
|
||||
"errors": ["Optional array of validation errors"]
|
||||
}
|
||||
```
|
||||
|
||||
Common error codes include `unauthorized`, `not_found`, `validation_failed`, and `internal_server_error`.
|
||||
Reference in New Issue
Block a user