This document outlines the available API endpoints for the React frontend.
Base URL: /api (Relative to the application root)
All requests should include the following headers to ensure correct JSON handling and authentication with Laravel Sanctum:
Accept: application/json (Required to receive JSON responses instead of redirects on errors)Content-Type: application/json (Required when sending JSON data in the request body)Authorization: Bearer <access_token> (Required for protected routes)API requests may return error responses with appropriate HTTP status codes and JSON bodies.
Returned when request data fails validation rules.
{
"message": "The given data was invalid.",
"errors": {
"field_name": [
"The field name field is required."
]
}
}
Returned when authentication fails (e.g., missing or invalid token).
{
"message": "Unauthenticated."
}
Returned when the user is authenticated but does not have permission to perform the action.
{
"message": "This action is unauthorized."
}
Create a new user account.
Endpoint: POST /register
cURL:
curl -X POST http://tuohua-work-api.test/api/register \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@tuohuacm.com", "password": "secret_password", "password_confirmation": "secret_password"}'
Request Body:
{
"name": "John Doe",
"email": "john@tuohuacm.com",
"password": "secret_password",
"password_confirmation": "secret_password"
}
name (string, required): User's full name.email (string, required): Valid email address.password (string, required): Min 8 characters.password_confirmation (string, required): Must match password.Response (201 Created):
{
"access_token": "1|laravel_sanctum_token...",
"token_type": "Bearer",
"user": {
"id": 1,
"name": "John Doe",
"email": "john@tuohuacm.com",
"created_at": "...",
"updated_at": "..."
}
}
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., duplicate email, invalid password, non-tuohuacm.com email).
Authenticate a user.
Endpoint: POST /login
cURL:
curl -X POST http://tuohua-work-api.test/api/login \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"email": "john@tuohuacm.com", "password": "secret_password"}'
Request Body:
{
"email": "john@tuohuacm.com",
"password": "secret_password"
}
email (string, required)password (string, required)Response (200 OK):
{
"access_token": "2|laravel_sanctum_token...",
"token_type": "Bearer",
"user": {
"id": 1,
"name": "John Doe",
"email": "john@tuohuacm.com",
"..."
}
}
Error Response (422 Unprocessable Entity): See Error Responses for invalid credentials.
Invalidate the current session token.
POST /logoutcurl -X POST http://tuohua-work-api.test/api/logout \
-H "Accept: application/json" \
-H "Authorization: Bearer <access_token>"
Authorization: Bearer <access_token>{
"message": "Logged out successfully"
}
Get the DingTalk OAuth login URL.
Endpoint: GET /auth/dingtalk/redirect
cURL:
curl -X GET http://tuohua-work-api.test/api/auth/dingtalk/redirect \
-H "Accept: application/json"
Response (200 OK):
{
"url": "https://login.dingtalk.com/oauth2/auth?redirect_uri=...&response_type=code&client_id=...&scope=openid%20corpid&state=...&prompt=consent",
"state": "random_state_string"
}
url (string): The DingTalk OAuth authorization URL to redirect the user to.state (string): Random state string for CSRF protection.Usage: Frontend should redirect the user to the url. After user authorizes, DingTalk will redirect back to the configured redirect_uri with a code parameter.
Handle the DingTalk OAuth callback and authenticate/register the user.
Endpoint: POST /auth/dingtalk/callback
cURL:
curl -X POST http://tuohua-work-api.test/api/auth/dingtalk/callback \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"code": "authorization_code_from_dingtalk"}'
Request Body:
{
"code": "authorization_code_from_dingtalk"
}
code (string, required): Authorization code returned by DingTalk after user authorization.Response (200 OK):
{
"access_token": "1|laravel_sanctum_token...",
"token_type": "Bearer",
"user": {
"id": 1,
"name": "zhangsan",
"email": "zhangsan@alibaba-inc.com",
"avatar": "https://xxx",
"dingtalk_open_id": "123",
"created_at": "...",
"updated_at": "..."
}
}
Error Response (400 Bad Request): See Error Responses for errors from DingTalk API (e.g., invalid code, failed to get user info).
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., missing code).
Fetch the currently authenticated user's details.
GET /usercurl -X GET http://tuohua-work-api.test/api/user \
-H "Accept: application/json" \
-H "Authorization: Bearer <access_token>"
Authorization: Bearer <access_token>{
"id": 1,
"name": "John Doe",
"email": "john@tuohuacm.com",
"email_verified_at": null,
"created_at": "2023-10-27T10:00:00.000000Z",
"updated_at": "2023-10-27T10:00:00.000000Z",
"is_admin": false
}
Update the user's name and/or email.
Endpoint: PUT /profile
cURL:
curl -X PUT http://tuohua-work-api.test/api/profile \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-d '{"name": "Jane Doe", "email": "jane@tuohuacm.com"}'
Headers:
Authorization: Bearer <access_token>Request Body:
{
"name": "Jane Doe",
"email": "jane@tuohuacm.com"
}
name (string, optional): User's new full name.email (string, optional): User's new email address.Response (200 OK):
{
"message": "Profile updated successfully.",
"user": {
"id": 1,
"name": "Jane Doe",
"email": "jane@tuohuacm.com",
"..."
}
}
Error Response (401 Unauthorized): See Error Responses if authentication token is missing or invalid.
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., duplicate email, invalid email format).
Change the user's password.
Endpoint: PUT /profile/password
cURL:
curl -X PUT http://tuohua-work-api.test/api/profile/password \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-d '{"current_password": "old_password", "password": "new_secure_password", "password_confirmation": "new_secure_password"}'
Headers:
Authorization: Bearer <access_token>Request Body:
{
"current_password": "old_password",
"password": "new_secure_password",
"password_confirmation": "new_secure_password"
}
current_password (string, required): Must match the current password.password (string, required): New password (min 8 chars).password_confirmation (string, required): Must match password.Response (200 OK):
{
"message": "Password updated successfully."
}
Error Response (401 Unauthorized): See Error Responses if authentication token is missing or invalid.
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., current password mismatch, new password not confirmed).
Get a paginated list of all users. Only users with is_admin set to true can access this endpoint.
GET /userscurl -X GET http://tuohua-work-api.test/api/users \
-H "Accept: application/json" \
-H "Authorization: Bearer <access_token>"
Authorization: Bearer <access_token>search (optional): Search users by name or email.page (optional): Page number (default: 1).{
"current_page": 1,
"data": [
{
"id": 1,
"name": "John Doe",
"email": "john@tuohuacm.com",
"is_admin": false,
"created_at": "2023-10-27T10:00:00.000000Z",
"updated_at": "2023-10-27T10:00:00.000000Z"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@tuohuacm.com",
"is_admin": true,
"created_at": "2023-10-28T10:00:00.000000Z",
"updated_at": "2023-10-28T10:00:00.000000Z"
}
],
"total": 2,
"per_page": 20,
"last_page": 1
// ... pagination meta
}
Update any user's is_admin attribute. Only users with is_admin set to true can access this endpoint.
Endpoint: PUT /users/{id}/admin-status
cURL:
curl -X PUT http://tuohua-work-api.test/api/users/123/admin-status \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-d '{"is_admin": true}'
Headers:
Authorization: Bearer <access_token>Request Body:
{
"is_admin": true
}
is_admin (boolean, required): The new admin status for the user.Response (200 OK):
{
"id": 123,
"name": "Jane Doe",
"email": "jane@tuohuacm.com",
"is_admin": true,
"created_at": "...",
"updated_at": "..."
}
Error Response (401 Unauthorized): See Error Responses if authentication token is missing or invalid.
Error Response (403 Forbidden): See Error Responses if the current user is not an admin.
Error Response (404 Not Found): If the target user does not exist.
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., missing or invalid is_admin field).
Upload an image or PDF file (Max 10MB).
POST /uploadAuthorization: Bearer <token>Content-Type: multipart/form-datafile (File, required): The file to upload (jpg, jpeg, png, webp, pdf).{
"path": "uploads/hash.jpg",
"url": "http://tuohua-work-api.test/storage/uploads/hash.jpg"
}
Get a paginated list of the current user's reimbursement reports.
GET /reimbursementsAuthorization: Bearer <token>status (optional): Filter by status (e.g., draft, submitted, approved, rejected).page (optional): Page number (default: 1).{
"current_page": 1,
"data": [
{
"id": 1,
"title": "December 2025 Expenses",
"status": "draft",
"total_amount_cny": "1250.00",
"created_at": "...",
"user": { ... }
}
],
"total": 1
// ... pagination meta
}
Get a paginated list of all users' reimbursement reports (default excludes drafts).
GET /reimbursements/allAuthorization: Bearer <token>user_id (optional): Filter by specific user.status (optional): Filter by status.Create a new reimbursement group (e.g., "Jan 2025").
POST /reimbursements{
"title": "January 2025 Trip"
}
Get details of a specific report, including its items.
GET /reimbursements/{id}{
"id": 1,
"title": "January 2025 Trip",
"items": [
{
"id": 10,
"type": "transport",
"amount_cny": "100.00",
"payment_proofs": ["url1", "url2"]
// ...
}
],
"offset_invoices": ["url_to_pdf"]
}
Update report details (title, offset invoices) while in draft or rejected status.
PUT /reimbursements/{id}{
"title": "Updated Title",
"offset_invoices": ["http://example.com/invoice.pdf"]
}
Submit a draft report for approval.
PUT /reimbursements/{id}/submitsubmitted.Delete a reimbursement report. Only the owner can delete their own report, and only when it's in draft status.
DELETE /reimbursements/{id}Authorization: Bearer <token>curl -X DELETE http://tuohua-work-api.test/api/reimbursements/1 \
-H "Accept: application/json" \
-H "Authorization: Bearer <access_token>"
{
"message": "Report deleted successfully"
}
draft status.
{
"message": "Only draft reports can be deleted."
}
Add a specific expense item to a report.
POST /reimbursements/{id}/items{
"type": "transport", // transport, accommodation, general
"date": "2025-12-01",
"currency": "CNY",
"amount_original": 100.50, // Optional, only needed for non-CNY currencies
"amount_cny": 100.50,
"description": "Taxi to airport",
"payment_proofs": ["http://url.to/image.jpg"], // Required array
"invoice_files": ["http://url.to/invoice.pdf"], // Optional array
"meta": { "start": "Home", "end": "Airport" } // Optional generic data
}
Update an existing item.
PUT /reimbursements/{reportId}/items/{itemId}Remove an item from a draft report.
DELETE /reimbursements/{reportId}/items/{itemId}{"message": "Item deleted"}Approve a submitted report.
PUT /reimbursements/{id}/approveapproved, audited_at, and auditor_id.Reject a submitted report.
PUT /reimbursements/{id}/reject{
"reason": "Missing invoices for hotel."
}
rejected.Fetch all records from a specified DingTalk AI Table (Notable). Supports generic filtering.
Endpoint: GET /dingtalk/table
Headers: Authorization: Bearer <token>
cURL:
curl -X GET "http://tuohua-work-api.test/api/dingtalk/table?table_id=TABLE_ID&sheet_id=SHEET_ID&Status=Active" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json"
Query Params:
table_id (string, required): The ID of the DingTalk Table App (Node ID).sheet_id (string, required): The ID of the specific Sheet.[Column Name] (optional): Any other query parameter is treated as a filter for that column name. (e.g., Status=Active, Name=John).Response (200 OK):
{
"success": true,
"data": [
{
"_index": 1,
"Name": "John Doe",
"Status": "Active",
"Phone": "13800138000",
"Created At": "2025-01-01"
},
// ...
]
}
Error Response (422 Unprocessable Entity): Missing table_id or sheet_id.
{
"success": false,
"message": "Validation Error",
"errors": {
"table_id": ["The table id field is required."]
}
}
Error Response (500 Internal Server Error): DingTalk API error or other server issues.
{
"success": false,
"message": "DingTalk API Error (invalidRequest.resource.notFound): The requested resource was not found..."
}
Sends a request for chat completion to a Large Language Model (LLM) service.
Endpoint: POST /llm/completion
Headers:
Authorization: Bearer <token> (if protected by auth middleware)cURL:
curl -X POST "http://tuohua-work-api.test/api/llm/completion" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-plus",
"prompt": "Tell me a story about a brave knight."
// "image_url": "http://example.com/image.jpg" // DashScope models might have different multi-modal input formats
}'
(Example with messages array)
curl -X POST "http://tuohua-work-api.test/api/llm/completion" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-plus",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
]
}'
(Example with messages array and multi-modal content - adjust according to specific DashScope model capabilities and format)
curl -X POST "http://tuohua-work-api.test/api/llm/completion" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-vl-plus", // Example for a multi-modal Qwen model
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What do you see in this image?"},
// DashScope multi-modal input for image might differ from OpenAI's image_url
// Refer to DashScope API docs for exact format (e.g., {"image": "base64_encoded_image_string"} or {"image_url": "http://url.to/image.jpg"})
{"image": "http://example.com/image.jpg"} // Placeholder, verify actual DashScope format
]
}
]
}'
Request Body:
{
"model": "string",
"messages": [
{
"role": "string",
"content": "string | array"
}
],
"prompt": "string",
"image_url": "string | null", // Note: DashScope multi-modal input might differ, verify with DashScope docs.
"temperature": "number", // Optional, example of other forwarded parameters
"top_p": "number" // Optional, DashScope equivalent to OpenAI's 'top_p'
}
model (string, required): The identifier of the LLM model to use (e.g., qwen-plus, qwen-max).messages (array, optional): An array of message objects representing the conversation history. Each object must have a role (e.g., user, assistant, system) and content. content can be a string for text-only messages or an array of objects for multi-modal messages. If messages is provided, the prompt and image_url fields are ignored for message construction.prompt (string, required if messages is not present): A text prompt for the LLM. If image_url is also provided, they will be combined into a multi-modal user message.image_url (string, nullable, optional): A publicly accessible URL to an image. This is used in conjunction with the prompt field to create a multi-modal user message. Note: DashScope's specific format for multi-modal input (e.g., image_url within content array) might differ from OpenAI's. Please refer to DashScope's official API documentation for the exact multi-modal input structure.temperature, top_p, seed) will be forwarded directly to the underlying LLM service if supported by the service and the OpenAIService (or equivalent DashScope service) implementation.Response (200 OK): The response structure directly mirrors the JSON response from the underlying LLM service (e.g., DashScope's chat completion API).
{
"output": {
"text": null,
"finish_reason": "stop",
"choices": [
{
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "Once upon a time, in a land far away, lived a brave knight..."
}
}
]
},
"usage": {
"input_tokens": 12,
"output_tokens": 50,
"total_tokens": 62
},
"request_id": "YOUR_REQUEST_ID"
// ... other fields as per DashScope LLM service response
}
(Example with gpt-4o for comparison)
// OpenAI Example Response
// {
// "id": "chatcmpl-...",
// "object": "chat.completion",
// "created": 1677652288,
// "model": "gpt-4o",
// "choices": [
// {
// "index": 0,
// "message": {
// "role": "assistant",
// "content": "Once upon a time, in a land far away, lived a brave knight..."
// },
// "logprobs": null,
// "finish_reason": "stop"
// }
// ],
// "usage": {
// "prompt_tokens": 20,
// "completion_tokens": 100,
// "total_tokens": 120
// }
// }
Error Response (422 Unprocessable Entity): See Error Responses for validation errors (e.g., missing model, both messages and prompt are missing).
Error Response (4xx/5xx from LLM Service): The API will forward error responses directly from the LLM service. (Example from DashScope)
{
"code": "InvalidParameter",
"message": "The parameter 'input.messages.0.content' is invalid.",
"request_id": "YOUR_REQUEST_ID"
}
(Example from OpenAI for comparison)
// OpenAI Example Error Response
// {
// "error": {
// "message": "Incorrect API key provided: sk-...",
// "type": "invalid_request_error",
// "param": null,
// "code": "invalid_api_key"
// }
// }
This section outlines API endpoints for managing water pump store signboard information. Each record is associated with the user who created it, ensuring data independence.
Get a paginated list of the current user's store signboard records.
GET /store-signboardsAuthorization: Bearer <token>page (optional): Page number (default: 1).per_page (optional): Items per page (default: 20).curl -X GET "http://tuohua-work-api.test/api/store-signboards?page=1" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json"
{
"current_page": 1,
"data": [
{
"id": 1,
"user_id": 1,
"company_name": "Acme Pumps",
"company_address": "123 Water St, Pumpville",
"contact_info": "555-1234",
"remarks": "Large red signboard, visible from main road.",
"image_path": "uploads/signboards/acme_pumps_1.jpg",
"created_at": "2025-12-17T08:00:00.000000Z",
"updated_at": "2025-12-17T08:00:00.000000Z"
},
{
"id": 2,
"user_id": 1,
"company_name": "Hydro Solutions",
"image_path": "uploads/signboards/hydro_solutions_1.png",
"created_at": "2025-12-17T09:00:00.000000Z",
"updated_at": "2025-12-17T09:00:00.000000Z"
}
],
"first_page_url": "http://tuohua-work-api.test/api/store-signboards?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http://tuohua-work-api.test/api/store-signboards?page=1",
"links": [
{ "url": null, "label": "« Previous", "active": false },
{ "url": "http://tuohua-work-api.test/api/store-signboards?page=1", "label": "1", "active": true },
{ "url": null, "label": "Next »", "active": false }
],
"next_page_url": null,
"path": "http://tuohua-work-api.test/api/store-signboards",
"per_page": 20,
"prev_page_url": null,
"to": 2,
"total": 2
}
Create a new store signboard record. The image_path should be obtained by first uploading the image using the /api/upload endpoint.
POST /store-signboardsAuthorization: Bearer <token>curl -X POST "http://tuohua-work-api.test/api/store-signboards" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_name": "New Pump Co.",
"company_address": "789 Lake Blvd",
"contact_info": "manager@newpump.com",
"remarks": "Recently opened, modern storefront.",
"image_path": "uploads/new_pump_co.webp"
}'
{
"company_name": "New Pump Co.",
"company_address": "789 Lake Blvd",
"contact_info": "manager@newpump.com",
"remarks": "Recently opened, modern storefront.",
"image_path": "uploads/new_pump_co.webp"
}
company_name (string, required): Name of the company.company_address (string, nullable): Address of the company.contact_info (string, nullable): Contact details (phone, email, etc.).remarks (string, nullable): Any additional notes.image_path (string, required): Path to the uploaded signboard image (e.g., from /api/upload).{
"id": 3,
"user_id": 1,
"company_name": "New Pump Co.",
"company_address": "789 Lake Blvd",
"contact_info": "manager@newpump.com",
"remarks": "Recently opened, modern storefront.",
"image_path": "uploads/new_pump_co.webp",
"created_at": "2025-12-17T10:00:00.000000Z",
"updated_at": "2025-12-17T10:00:00.000000Z"
}
Get details of a specific store signboard. Only the owner or an admin can access this.
GET /store-signboards/{id}Authorization: Bearer <token>curl -X GET "http://tuohua-work-api.test/api/store-signboards/1" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json"
{
"id": 1,
"user_id": 1,
"company_name": "Acme Pumps",
"company_address": "123 Water St, Pumpville",
"contact_info": "555-1234",
"remarks": "Large red signboard, visible from main road.",
"image_path": "uploads/signboards/acme_pumps_1.jpg",
"created_at": "2025-12-17T08:00:00.000000Z",
"updated_at": "2025-12-17T08:00:00.000000Z"
}
Update details of a specific store signboard. Only the owner or an admin can update this.
PUT /store-signboards/{id}Authorization: Bearer <token>curl -X PUT "http://tuohua-work-api.test/api/store-signboards/1" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"company_name": "Acme Pumps Inc.",
"contact_info": "555-5678"
}'
{
"company_name": "Acme Pumps Inc.",
"company_address": "123 Water St, Pumpville",
"contact_info": "555-5678",
"remarks": "Large red signboard, visible from main road.",
"image_path": "uploads/signboards/acme_pumps_1_new.jpg"
}
image_path can also be updated.{
"id": 1,
"user_id": 1,
"company_name": "Acme Pumps Inc.",
"company_address": "123 Water St, Pumpville",
"contact_info": "555-5678",
"remarks": "Large red signboard, visible from main road.",
"image_path": "uploads/signboards/acme_pumps_1_new.jpg",
"created_at": "2025-12-17T08:00:00.000000Z",
"updated_at": "2025-12-17T11:00:00.000000Z"
}
Delete a specific store signboard record. Only the owner or an admin can delete this.
DELETE /store-signboards/{id}Authorization: Bearer <token>curl -X DELETE "http://tuohua-work-api.test/api/store-signboards/1" \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json"
{
"message": "Deleted successfully"
}