From f523f0fbc91604505a837f7bd4c74b414ac7bb45 Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:36:20 +0200 Subject: [PATCH] add api spec and mock api server --- src/api/.gitignore | 2 + src/api/README.md | 157 +++ src/api/openapi.yaml | 1743 +++++++++++++++++++++++++++++ src/api/openapi_updated.yaml | 2032 ++++++++++++++++++++++++++++++++++ src/api/package.json | 30 + src/api/serve.sh | 40 + src/api/server.js | 175 +++ src/api/start-mock-server.sh | 75 ++ src/api/swagger-ui.html | 153 +++ src/api/test-mock-api.sh | 153 +++ 10 files changed, 4560 insertions(+) create mode 100644 src/api/.gitignore create mode 100644 src/api/README.md create mode 100644 src/api/openapi.yaml create mode 100644 src/api/openapi_updated.yaml create mode 100644 src/api/package.json create mode 100755 src/api/serve.sh create mode 100644 src/api/server.js create mode 100755 src/api/start-mock-server.sh create mode 100644 src/api/swagger-ui.html create mode 100755 src/api/test-mock-api.sh diff --git a/src/api/.gitignore b/src/api/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/src/api/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/src/api/README.md b/src/api/README.md new file mode 100644 index 0000000..a9f3663 --- /dev/null +++ b/src/api/README.md @@ -0,0 +1,157 @@ +# Freezone Backend API + +A comprehensive API for managing digital residents, free zone companies, invoices, and expenses. Designed for authorized resellers to register and manage entities in the freezone system. + +## Quick Start + +### 1. Authentication +```bash +curl -X POST https://api.freezone.com/v1/auth/api-key \ + -H "Content-Type: application/json" \ + -d '{ + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "access_password": "freezone_access_password" + }' +``` + +### 2. Use API Key +```bash +curl -H "X-API-Key: your_api_key" https://api.freezone.com/v1/digital-residents +``` + +## Key Concepts + +### Dual ID System +- **Input**: Provide your reseller IDs when creating entities +- **Output**: Receive Freezone-issued IDs for all operations +- **Operations**: Use Freezone IDs for all subsequent CRUD operations + +| Entity | Reseller ID (Input) | Freezone ID (Operations) | +|--------|-------------------|-------------------------| +| Digital Resident | `reseller_user_id` | `resident_id` | +| Free Zone Company | `reseller_company_id` | `fzc_id` | +| Invoice | `reseller_invoice_id` | `fz_invoice_id` | +| Expense | `reseller_expense_id` | `fz_expense_id` | + +### Prerequisites & Dependencies + +#### Creating Free Zone Companies +- **Required**: All `shareholder_resident_ids` must be registered digital residents +- **Validation**: API validates that all provided resident IDs exist before company creation +- **Example**: To create a company with shareholders `["fz_resident_abc123", "fz_resident_def456"]`, both residents must exist + +#### Creating Invoices/Expenses +- **Required**: Valid `fzc_id` (company must exist) +- **Scope**: All invoices and expenses are company-scoped + +## API Endpoints + +### Digital Residents +``` +GET /digital-residents # List residents +POST /digital-residents # Create (provide reseller_user_id) +GET /digital-residents/{resident_id} # Get by Freezone ID +PUT /digital-residents/{resident_id} # Update by Freezone ID +DELETE /digital-residents/{resident_id} # Delete by Freezone ID +``` + +### Free Zone Companies +``` +GET /free-zone-companies # List companies +POST /free-zone-companies # Create (provide reseller_company_id) +GET /free-zone-companies/{fzc_id} # Get by Freezone ID +PUT /free-zone-companies/{fzc_id} # Update by Freezone ID +DELETE /free-zone-companies/{fzc_id} # Delete by Freezone ID +``` + +### Invoices (Company-scoped) +``` +GET /free-zone-companies/{fzc_id}/invoices # List invoices +POST /free-zone-companies/{fzc_id}/invoices # Create invoice +GET /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Get invoice +PUT /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Update invoice +DELETE /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Delete invoice +``` + +### Expenses (Company-scoped) +``` +GET /free-zone-companies/{fzc_id}/expenses # List expenses +POST /free-zone-companies/{fzc_id}/expenses # Create expense +GET /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Get expense +PUT /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Update expense +DELETE /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Delete expense +``` + +## Example Workflow + +### 1. Create Digital Residents +```json +POST /digital-residents +{ + "reseller_user_id": "reseller_user_123", + "email": "john@example.com", + "first_name": "John", + "last_name": "Doe", + "kyc_provider_id": "kyc_john_789" +} +// Returns: resident_id: "fz_resident_abc123" +``` + +### 2. Create Free Zone Company +```json +POST /free-zone-companies +{ + "reseller_company_id": "reseller_comp_456", + "name": "Tech Innovations FZC", + "type": "company", + "shareholder_resident_ids": ["fz_resident_abc123"], // Must exist! + "crypto_wallets": [ + { + "public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", + "chain": "bitcoin", + "label": "Main Bitcoin Wallet" + } + ] +} +// Returns: fzc_id: "fz_company_xyz789" +``` + +### 3. Create Invoice +```json +POST /free-zone-companies/fz_company_xyz789/invoices +{ + "reseller_invoice_id": "reseller_inv_789", + "invoice_number": "INV-2024-001", + "amount": 1500.00, + "currency": "USD", + "issue_date": "2024-01-15" +} +// Returns: fz_invoice_id: "fz_invoice_inv123" +``` + +## Rate Limiting +- **Limit**: 100 requests per minute +- **Headers**: Check `X-RateLimit-Remaining` and `X-RateLimit-Reset` + +## Error Handling +- **400**: Bad request (validation errors) +- **401**: Invalid/missing API key +- **404**: Resource not found +- **409**: Conflict (duplicate reseller IDs) +- **429**: Rate limit exceeded + +## KYC Integration +Use the `kyc_provider_id` from digital residents to query your KYC provider for verification data. + +## Crypto Wallet Support +Supported chains: `bitcoin`, `ethereum`, `polygon`, `binance_smart_chain`, `solana` + +## Documentation +- **Interactive API Docs**: [Swagger UI](./swagger-ui.html) +- **OpenAPI Spec**: [openapi_updated.yaml](./openapi_updated.yaml) + +## Support +- **Email**: api-support@freezone.com +- **Rate Limiting**: 100 requests/minute +- **Authentication**: API key required for all endpoints diff --git a/src/api/openapi.yaml b/src/api/openapi.yaml new file mode 100644 index 0000000..11b3962 --- /dev/null +++ b/src/api/openapi.yaml @@ -0,0 +1,1743 @@ +openapi: 3.0.3 +info: + title: Freezone Backend API + description: | + API for the Freezone backend system. Provides endpoints for managing digital residents, + free zone companies, invoices, and expenses. Designed for use by authorized resellers to + register digital residents and free zone companies. + + **ID System**: + - Resellers provide their own IDs when creating entities + - API returns Freezone-issued IDs for all created entities + - All subsequent operations use Freezone-issued IDs + version: 1.0.0 + contact: + name: Freezone API Support + email: api-support@freezone.com + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: https://api.freezone.com/v1 + description: Production server + - url: https://staging-api.freezone.com/v1 + description: Staging server + - url: http://localhost:8080/v1 + description: Development server + +security: + - ApiKeyAuth: [] + +paths: + # Authentication endpoints + /auth/api-key: + post: + tags: + - Authentication + summary: Generate API key + description: | + Generate a new API key for authentication. This endpoint is password-protected + and only available to authorized resellers with the correct access password. + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - client_id + - client_secret + - access_password + properties: + client_id: + type: string + description: Client identifier provided by Freezone + example: "reseller_123" + client_secret: + type: string + description: Client secret provided by Freezone + example: "secret_abc123xyz" + access_password: + type: string + description: Special access password for API key generation + example: "freezone_access_2024" + responses: + '200': + description: API key generated successfully + content: + application/json: + schema: + type: object + properties: + api_key: + type: string + description: Generated API key + example: "fz_api_key_abc123def456ghi789" + expires_at: + type: string + format: date-time + description: API key expiration time + example: "2024-12-31T23:59:59Z" + example: + api_key: "fz_api_key_abc123def456ghi789" + expires_at: "2024-12-31T23:59:59Z" + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + # Digital Resident endpoints + /digital-residents: + get: + tags: + - Digital Residents + summary: List digital residents + description: Retrieve a paginated list of digital residents + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of digital residents per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: search + in: query + description: Search query for resident name or email + schema: + type: string + responses: + '200': + description: Digital residents retrieved successfully + content: + application/json: + schema: + type: object + properties: + digital_residents: + type: array + items: + $ref: '#/components/schemas/DigitalResident' + pagination: + $ref: '#/components/schemas/Pagination' + example: + digital_residents: + - resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 25 + total_pages: 3 + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Digital Residents + summary: Create digital resident + description: | + Create a new digital resident. The reseller provides their own user ID, + and the API returns a Freezone-issued resident ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDigitalResidentRequest' + example: + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + responses: + '201': + description: Digital resident created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + + /digital-residents/{resident_id}: + get: + tags: + - Digital Residents + summary: Get digital resident + description: Retrieve a specific digital resident by Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + responses: + '200': + description: Digital resident retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Digital Residents + summary: Update digital resident + description: Update an existing digital resident using Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDigitalResidentRequest' + example: + email: "john.doe.updated@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_updated_789" + phone: "+1234567891" + status: "active" + responses: + '200': + description: Digital resident updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe.updated@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_updated_789" + phone: "+1234567891" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Digital Residents + summary: Delete digital resident + description: Delete a digital resident using Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + responses: + '204': + description: Digital resident deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Free Zone Company endpoints + /free-zone-companies: + get: + tags: + - Free Zone Companies + summary: List free zone companies + description: Retrieve a paginated list of free zone companies + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of free zone companies per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: search + in: query + description: Search query for company name + schema: + type: string + responses: + '200': + description: Free zone companies retrieved successfully + content: + application/json: + schema: + type: object + properties: + free_zone_companies: + type: array + items: + $ref: '#/components/schemas/FreeZoneCompany' + pagination: + $ref: '#/components/schemas/Pagination' + example: + free_zone_companies: + - fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 15 + total_pages: 2 + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Free Zone Companies + summary: Create free zone company + description: | + Create a new free zone company. The reseller provides their own company ID, + and the API returns a Freezone-issued company ID (fzc_id). + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreeZoneCompanyRequest' + example: + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + responses: + '201': + description: Free zone company created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + + /free-zone-companies/{fzc_id}: + get: + tags: + - Free Zone Companies + summary: Get free zone company + description: Retrieve a specific free zone company by Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + responses: + '200': + description: Free zone company retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Free Zone Companies + summary: Update free zone company + description: Update an existing free zone company using Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateFreeZoneCompanyRequest' + example: + name: "Tech Innovations FZC Updated" + type: "company" + description: "Updated technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "456 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456", "fz_resident_ghi789"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + responses: + '200': + description: Free zone company updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC Updated" + type: "company" + description: "Updated technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "456 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456", "fz_resident_ghi789"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Free Zone Companies + summary: Delete free zone company + description: Delete a free zone company using Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + responses: + '204': + description: Free zone company deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Invoice endpoints + /free-zone-companies/{fzc_id}/invoices: + get: + tags: + - Invoices + summary: List company invoices + description: Retrieve a paginated list of invoices for a free zone company + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of invoices per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: status + in: query + description: Filter by invoice status + schema: + type: string + enum: [draft, sent, paid, overdue, cancelled] + responses: + '200': + description: Invoices retrieved successfully + content: + application/json: + schema: + type: object + properties: + invoices: + type: array + items: + $ref: '#/components/schemas/FreeZoneInvoice' + pagination: + $ref: '#/components/schemas/Pagination' + example: + invoices: + - fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + status: "sent" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 5 + total_pages: 1 + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Invoices + summary: Create invoice + description: | + Create a new invoice for a free zone company. The reseller provides their own invoice ID, + and the API returns a Freezone-issued invoice ID (fz_invoice_id). + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreeZoneInvoiceRequest' + example: + reseller_invoice_id: "reseller_inv_456" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + issue_date: "2024-01-15" + due_date: "2024-02-15" + responses: + '201': + description: Invoice created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneInvoice' + example: + fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + status: "draft" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + /companies/{company_id}/invoices/{invoice_id}: + get: + tags: + - Invoices + summary: Get invoice + description: Retrieve a specific invoice + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: invoice_id + in: path + required: true + description: Invoice ID + schema: + type: string + format: uuid + responses: + '200': + description: Invoice retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Invoices + summary: Update invoice + description: Update an existing invoice + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: invoice_id + in: path + required: true + description: Invoice ID + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateInvoiceRequest' + responses: + '200': + description: Invoice updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Invoices + summary: Delete invoice + description: Delete an invoice + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: invoice_id + in: path + required: true + description: Invoice ID + schema: + type: string + format: uuid + responses: + '204': + description: Invoice deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Expense endpoints + /companies/{company_id}/expenses: + get: + tags: + - Expenses + summary: List company expenses + description: Retrieve a paginated list of expenses for a company + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of expenses per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: category + in: query + description: Filter by expense category + schema: + type: string + responses: + '200': + description: Expenses retrieved successfully + content: + application/json: + schema: + type: object + properties: + expenses: + type: array + items: + $ref: '#/components/schemas/Expense' + pagination: + $ref: '#/components/schemas/Pagination' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Expenses + summary: Create expense + description: Create a new expense for a company + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateExpenseRequest' + responses: + '201': + description: Expense created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Expense' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + /companies/{company_id}/expenses/{expense_id}: + get: + tags: + - Expenses + summary: Get expense + description: Retrieve a specific expense + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: expense_id + in: path + required: true + description: Expense ID + schema: + type: string + format: uuid + responses: + '200': + description: Expense retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Expense' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Expenses + summary: Update expense + description: Update an existing expense + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: expense_id + in: path + required: true + description: Expense ID + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateExpenseRequest' + responses: + '200': + description: Expense updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Expense' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Expenses + summary: Delete expense + description: Delete an expense + parameters: + - name: company_id + in: path + required: true + description: Company ID + schema: + type: string + format: uuid + - name: expense_id + in: path + required: true + description: Expense ID + schema: + type: string + format: uuid + responses: + '204': + description: Expense deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key + description: API key for authentication + + schemas: + # User schemas + User: + type: object + required: + - id + - email + - kyc_provider_id + - status + - created_at + - updated_at + properties: + id: + type: string + format: uuid + description: Unique user identifier + email: + type: string + format: email + description: User email address + first_name: + type: string + description: User first name + last_name: + type: string + description: User last name + kyc_provider_id: + type: string + description: ID used to query the KYC provider for user's KYC information + phone: + type: string + description: User phone number + status: + type: string + enum: [active, inactive, pending, suspended] + description: User status + created_at: + type: string + format: date-time + description: User creation timestamp + updated_at: + type: string + format: date-time + description: User last update timestamp + + CreateUserRequest: + type: object + required: + - email + - first_name + - last_name + - kyc_provider_id + properties: + email: + type: string + format: email + description: User email address + first_name: + type: string + description: User first name + last_name: + type: string + description: User last name + kyc_provider_id: + type: string + description: ID used to query the KYC provider for user's KYC information + phone: + type: string + description: User phone number + + UpdateUserRequest: + type: object + properties: + email: + type: string + format: email + description: User email address + first_name: + type: string + description: User first name + last_name: + type: string + description: User last name + kyc_provider_id: + type: string + description: ID used to query the KYC provider for user's KYC information + phone: + type: string + description: User phone number + status: + type: string + enum: [active, inactive, pending, suspended] + description: User status + + # Company schemas + Company: + type: object + required: + - id + - name + - type + - status + - shareholder_user_ids + - crypto_wallets + - created_at + - updated_at + properties: + id: + type: string + format: uuid + description: Unique company identifier + name: + type: string + description: Company name + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Company type + description: + type: string + description: Company description + registration_number: + type: string + description: Company registration number + tax_id: + type: string + description: Company tax identification number + address: + $ref: '#/components/schemas/Address' + shareholder_user_ids: + type: array + items: + type: string + format: uuid + description: Array of user IDs who are shareholders of this company + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + status: + type: string + enum: [active, inactive, pending, suspended] + description: Company status + created_at: + type: string + format: date-time + description: Company creation timestamp + updated_at: + type: string + format: date-time + description: Company last update timestamp + + CreateCompanyRequest: + type: object + required: + - name + - type + - shareholder_user_ids + properties: + name: + type: string + description: Company name + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Company type + description: + type: string + description: Company description + registration_number: + type: string + description: Company registration number + tax_id: + type: string + description: Company tax identification number + address: + $ref: '#/components/schemas/Address' + shareholder_user_ids: + type: array + items: + type: string + format: uuid + description: Array of user IDs who are shareholders of this company + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + + UpdateCompanyRequest: + type: object + properties: + name: + type: string + description: Company name + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Company type + description: + type: string + description: Company description + registration_number: + type: string + description: Company registration number + tax_id: + type: string + description: Company tax identification number + address: + $ref: '#/components/schemas/Address' + shareholder_user_ids: + type: array + items: + type: string + format: uuid + description: Array of user IDs who are shareholders of this company + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + status: + type: string + enum: [active, inactive, pending, suspended] + description: Company status + + # Invoice schemas + Invoice: + type: object + required: + - id + - company_id + - invoice_number + - amount + - currency + - status + - issue_date + - created_at + - updated_at + properties: + id: + type: string + format: uuid + description: Unique invoice identifier + company_id: + type: string + format: uuid + description: Company ID this invoice belongs to + invoice_number: + type: string + description: Human-readable invoice number + amount: + type: number + format: decimal + description: Invoice amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + tax_amount: + type: number + format: decimal + description: Tax amount + description: + type: string + description: Invoice description + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + status: + type: string + enum: [draft, sent, paid, overdue, cancelled] + description: Invoice status + issue_date: + type: string + format: date + description: Invoice issue date + due_date: + type: string + format: date + description: Invoice due date + paid_date: + type: string + format: date + description: Invoice payment date + created_at: + type: string + format: date-time + description: Invoice creation timestamp + updated_at: + type: string + format: date-time + description: Invoice last update timestamp + + CreateInvoiceRequest: + type: object + required: + - invoice_number + - amount + - currency + - issue_date + properties: + invoice_number: + type: string + description: Human-readable invoice number + amount: + type: number + format: decimal + description: Invoice amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + tax_amount: + type: number + format: decimal + description: Tax amount + description: + type: string + description: Invoice description + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + issue_date: + type: string + format: date + description: Invoice issue date + due_date: + type: string + format: date + description: Invoice due date + + UpdateInvoiceRequest: + type: object + properties: + invoice_number: + type: string + description: Human-readable invoice number + amount: + type: number + format: decimal + description: Invoice amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + tax_amount: + type: number + format: decimal + description: Tax amount + description: + type: string + description: Invoice description + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + status: + type: string + enum: [draft, sent, paid, overdue, cancelled] + description: Invoice status + issue_date: + type: string + format: date + description: Invoice issue date + due_date: + type: string + format: date + description: Invoice due date + paid_date: + type: string + format: date + description: Invoice payment date + + # Expense schemas + Expense: + type: object + required: + - id + - company_id + - amount + - currency + - category + - date + - created_at + - updated_at + properties: + id: + type: string + format: uuid + description: Unique expense identifier + company_id: + type: string + format: uuid + description: Company ID this expense belongs to + amount: + type: number + format: decimal + description: Expense amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + category: + type: string + description: Expense category + description: + type: string + description: Expense description + vendor: + type: string + description: Vendor name + receipt_url: + type: string + format: uri + description: URL to receipt document + date: + type: string + format: date + description: Expense date + created_at: + type: string + format: date-time + description: Expense creation timestamp + updated_at: + type: string + format: date-time + description: Expense last update timestamp + + CreateExpenseRequest: + type: object + required: + - amount + - currency + - category + - date + properties: + amount: + type: number + format: decimal + description: Expense amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + category: + type: string + description: Expense category + description: + type: string + description: Expense description + vendor: + type: string + description: Vendor name + receipt_url: + type: string + format: uri + description: URL to receipt document + date: + type: string + format: date + description: Expense date + + UpdateExpenseRequest: + type: object + properties: + amount: + type: number + format: decimal + description: Expense amount + currency: + type: string + description: Currency code (e.g., USD, EUR) + category: + type: string + description: Expense category + description: + type: string + description: Expense description + vendor: + type: string + description: Vendor name + receipt_url: + type: string + format: uri + description: URL to receipt document + date: + type: string + format: date + description: Expense date + + # Supporting schemas + CryptoWallet: + type: object + required: + - public_key + - chain + properties: + public_key: + type: string + description: Wallet public key + chain: + type: string + enum: [bitcoin, ethereum, polygon, binance_smart_chain, solana] + description: Blockchain network + label: + type: string + description: Optional wallet label + + Address: + type: object + properties: + street: + type: string + description: Street address + city: + type: string + description: City + state: + type: string + description: State or province + postal_code: + type: string + description: Postal code + country: + type: string + description: Country code (ISO 3166-1 alpha-2) + + InvoiceLineItem: + type: object + required: + - description + - quantity + - unit_price + - amount + properties: + description: + type: string + description: Line item description + quantity: + type: number + description: Quantity + unit_price: + type: number + format: decimal + description: Unit price + amount: + type: number + format: decimal + description: Total amount for this line item + + Pagination: + type: object + required: + - page + - limit + - total + - total_pages + properties: + page: + type: integer + description: Current page number + limit: + type: integer + description: Number of items per page + total: + type: integer + description: Total number of items + total_pages: + type: integer + description: Total number of pages + + Error: + type: object + required: + - error + - message + properties: + error: + type: string + description: Error code + message: + type: string + description: Human-readable error message + details: + type: object + description: Additional error details + + responses: + BadRequest: + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "bad_request" + message: "Invalid request parameters" + + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "unauthorized" + message: "Invalid or missing API key" + + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "not_found" + message: "Resource not found" + + Conflict: + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "conflict" + message: "Resource already exists" + + RateLimited: + description: Rate limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "rate_limited" + message: "Rate limit exceeded. Try again later." + headers: + X-RateLimit-Limit: + description: Request limit per minute + schema: + type: integer + X-RateLimit-Remaining: + description: Remaining requests in current window + schema: + type: integer + X-RateLimit-Reset: + description: Time when rate limit resets + schema: + type: integer \ No newline at end of file diff --git a/src/api/openapi_updated.yaml b/src/api/openapi_updated.yaml new file mode 100644 index 0000000..c475dff --- /dev/null +++ b/src/api/openapi_updated.yaml @@ -0,0 +1,2032 @@ +openapi: 3.0.3 +info: + title: Freezone Backend API + description: | + API for the Freezone backend system. Provides endpoints for managing digital residents, + free zone companies, invoices, and expenses. Designed for use by authorized resellers to + register digital residents and free zone companies. + + **ID System**: + - Resellers provide their own IDs when creating entities + - API returns Freezone-issued IDs for all created entities + - All subsequent operations use Freezone-issued IDs + version: 1.0.0 + contact: + name: Freezone API Support + email: api-support@freezone.com + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: https://api.freezone.com/v1 + description: Production server + - url: https://staging-api.freezone.com/v1 + description: Staging server + - url: http://localhost:3000/api/mock + description: Mock server + +security: + - ApiKeyAuth: [] + +paths: + # Authentication endpoints + /auth/api-key: + post: + tags: + - Authentication + summary: Generate API key + description: | + Generate a new API key for authentication. This endpoint is password-protected + and only available to authorized resellers with the correct access password. + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - client_id + - client_secret + - access_password + properties: + client_id: + type: string + description: Client identifier provided by Freezone + example: "reseller_123" + client_secret: + type: string + description: Client secret provided by Freezone + example: "secret_abc123xyz" + access_password: + type: string + description: Special access password for API key generation + example: "freezone_access_2024" + responses: + '200': + description: API key generated successfully + content: + application/json: + schema: + type: object + properties: + api_key: + type: string + description: Generated API key + example: "fz_api_key_abc123def456ghi789" + expires_at: + type: string + format: date-time + description: API key expiration time + example: "2024-12-31T23:59:59Z" + example: + api_key: "fz_api_key_abc123def456ghi789" + expires_at: "2024-12-31T23:59:59Z" + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + # Digital Resident endpoints + /digital-residents: + get: + tags: + - Digital Residents + summary: List digital residents + description: Retrieve a paginated list of digital residents + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of digital residents per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: search + in: query + description: Search query for resident name or email + schema: + type: string + responses: + '200': + description: Digital residents retrieved successfully + content: + application/json: + schema: + type: object + properties: + digital_residents: + type: array + items: + $ref: '#/components/schemas/DigitalResident' + pagination: + $ref: '#/components/schemas/Pagination' + example: + digital_residents: + - resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 25 + total_pages: 3 + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Digital Residents + summary: Create digital resident + description: | + Create a new digital resident. The reseller provides their own user ID, + and the API returns a Freezone-issued resident ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDigitalResidentRequest' + example: + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + responses: + '201': + description: Digital resident created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + + /digital-residents/{resident_id}: + get: + tags: + - Digital Residents + summary: Get digital resident + description: Retrieve a specific digital resident by Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + responses: + '200': + description: Digital resident retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_789" + phone: "+1234567890" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Digital Residents + summary: Update digital resident + description: Update an existing digital resident using Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDigitalResidentRequest' + example: + email: "john.doe.updated@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_updated_789" + phone: "+1234567891" + status: "active" + responses: + '200': + description: Digital resident updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DigitalResident' + example: + resident_id: "fz_resident_abc123" + reseller_user_id: "reseller_user_456" + email: "john.doe.updated@example.com" + first_name: "John" + last_name: "Doe" + kyc_provider_id: "kyc_john_doe_updated_789" + phone: "+1234567891" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Digital Residents + summary: Delete digital resident + description: Delete a digital resident using Freezone-issued resident ID + parameters: + - name: resident_id + in: path + required: true + description: Freezone-issued resident ID + schema: + type: string + example: "fz_resident_abc123" + responses: + '204': + description: Digital resident deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Free Zone Company endpoints + /free-zone-companies: + get: + tags: + - Free Zone Companies + summary: List free zone companies + description: Retrieve a paginated list of free zone companies + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of free zone companies per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: search + in: query + description: Search query for company name + schema: + type: string + responses: + '200': + description: Free zone companies retrieved successfully + content: + application/json: + schema: + type: object + properties: + free_zone_companies: + type: array + items: + $ref: '#/components/schemas/FreeZoneCompany' + pagination: + $ref: '#/components/schemas/Pagination' + example: + free_zone_companies: + - fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 15 + total_pages: 2 + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Free Zone Companies + summary: Create free zone company + description: | + Create a new free zone company. The reseller provides their own company ID, + and the API returns a Freezone-issued company ID (fzc_id). + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreeZoneCompanyRequest' + example: + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + responses: + '201': + description: Free zone company created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + + /free-zone-companies/{fzc_id}: + get: + tags: + - Free Zone Companies + summary: Get free zone company + description: Retrieve a specific free zone company by Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + responses: + '200': + description: Free zone company retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC" + type: "company" + description: "Technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "123 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Free Zone Companies + summary: Update free zone company + description: Update an existing free zone company using Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateFreeZoneCompanyRequest' + example: + name: "Tech Innovations FZC Updated" + type: "company" + description: "Updated technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "456 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456", "fz_resident_ghi789"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + responses: + '200': + description: Free zone company updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneCompany' + example: + fzc_id: "fz_company_xyz789" + reseller_company_id: "reseller_comp_123" + name: "Tech Innovations FZC Updated" + type: "company" + description: "Updated technology consulting company" + registration_number: "FZC-2024-001" + tax_id: "TAX123456789" + address: + street: "456 Business Bay" + city: "Dubai" + state: "Dubai" + postal_code: "12345" + country: "AE" + shareholder_resident_ids: ["fz_resident_abc123", "fz_resident_def456", "fz_resident_ghi789"] + crypto_wallets: + - public_key: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: "bitcoin" + label: "Main Bitcoin Wallet" + - public_key: "0x742d35Cc6634C0532925a3b8D4C0C8b3C2e1e1e1" + chain: "ethereum" + label: "Main Ethereum Wallet" + status: "active" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Free Zone Companies + summary: Delete free zone company + description: Delete a free zone company using Freezone-issued company ID + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + responses: + '204': + description: Free zone company deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Invoice endpoints + /free-zone-companies/{fzc_id}/invoices: + get: + tags: + - Invoices + summary: List company invoices + description: Retrieve a paginated list of invoices for a free zone company + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of invoices per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: status + in: query + description: Filter by invoice status + schema: + type: string + enum: [draft, sent, paid, overdue, cancelled] + responses: + '200': + description: Invoices retrieved successfully + content: + application/json: + schema: + type: object + properties: + invoices: + type: array + items: + $ref: '#/components/schemas/FreeZoneInvoice' + pagination: + $ref: '#/components/schemas/Pagination' + example: + invoices: + - fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + status: "sent" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 5 + total_pages: 1 + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Invoices + summary: Create invoice + description: | + Create a new invoice for a free zone company. The reseller provides their own invoice ID, + and the API returns a Freezone-issued invoice ID (fz_invoice_id). + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreeZoneInvoiceRequest' + example: + reseller_invoice_id: "reseller_inv_456" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + issue_date: "2024-01-15" + due_date: "2024-02-15" + responses: + '201': + description: Invoice created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneInvoice' + example: + fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + status: "draft" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id}: + get: + tags: + - Invoices + summary: Get invoice + description: Retrieve a specific invoice using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_invoice_id + in: path + required: true + description: Freezone-issued invoice ID + schema: + type: string + example: "fz_invoice_inv123" + responses: + '200': + description: Invoice retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneInvoice' + example: + fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001" + amount: 1500.00 + currency: "USD" + tax_amount: 150.00 + description: "Consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 10 + unit_price: 150.00 + amount: 1500.00 + status: "sent" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Invoices + summary: Update invoice + description: Update an existing invoice using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_invoice_id + in: path + required: true + description: Freezone-issued invoice ID + schema: + type: string + example: "fz_invoice_inv123" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateFreeZoneInvoiceRequest' + example: + invoice_number: "INV-2024-001-UPDATED" + amount: 1650.00 + currency: "USD" + tax_amount: 165.00 + description: "Updated consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 11 + unit_price: 150.00 + amount: 1650.00 + status: "sent" + issue_date: "2024-01-15" + due_date: "2024-02-15" + responses: + '200': + description: Invoice updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneInvoice' + example: + fz_invoice_id: "fz_invoice_inv123" + reseller_invoice_id: "reseller_inv_456" + fzc_id: "fz_company_xyz789" + invoice_number: "INV-2024-001-UPDATED" + amount: 1650.00 + currency: "USD" + tax_amount: 165.00 + description: "Updated consulting services for Q1 2024" + line_items: + - description: "Strategy consulting" + quantity: 11 + unit_price: 150.00 + amount: 1650.00 + status: "sent" + issue_date: "2024-01-15" + due_date: "2024-02-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Invoices + summary: Delete invoice + description: Delete an invoice using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_invoice_id + in: path + required: true + description: Freezone-issued invoice ID + schema: + type: string + example: "fz_invoice_inv123" + responses: + '204': + description: Invoice deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + # Expense endpoints + /free-zone-companies/{fzc_id}/expenses: + get: + tags: + - Expenses + summary: List company expenses + description: Retrieve a paginated list of expenses for a free zone company + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of expenses per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + - name: category + in: query + description: Filter by expense category + schema: + type: string + responses: + '200': + description: Expenses retrieved successfully + content: + application/json: + schema: + type: object + properties: + expenses: + type: array + items: + $ref: '#/components/schemas/FreeZoneExpense' + pagination: + $ref: '#/components/schemas/Pagination' + example: + expenses: + - fz_expense_id: "fz_expense_exp123" + reseller_expense_id: "reseller_exp_456" + fzc_id: "fz_company_xyz789" + amount: 500.00 + currency: "USD" + category: "office_supplies" + description: "Office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123.pdf" + date: "2024-01-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + pagination: + page: 1 + limit: 10 + total: 8 + total_pages: 1 + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + post: + tags: + - Expenses + summary: Create expense + description: | + Create a new expense for a free zone company. The reseller provides their own expense ID, + and the API returns a Freezone-issued expense ID (fz_expense_id). + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreeZoneExpenseRequest' + example: + reseller_expense_id: "reseller_exp_456" + amount: 500.00 + currency: "USD" + category: "office_supplies" + description: "Office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123.pdf" + date: "2024-01-15" + responses: + '201': + description: Expense created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneExpense' + example: + fz_expense_id: "fz_expense_exp123" + reseller_expense_id: "reseller_exp_456" + fzc_id: "fz_company_xyz789" + amount: 500.00 + currency: "USD" + category: "office_supplies" + description: "Office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123.pdf" + date: "2024-01-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + /free-zone-companies/{fzc_id}/expenses/{fz_expense_id}: + get: + tags: + - Expenses + summary: Get expense + description: Retrieve a specific expense using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_expense_id + in: path + required: true + description: Freezone-issued expense ID + schema: + type: string + example: "fz_expense_exp123" + responses: + '200': + description: Expense retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneExpense' + example: + fz_expense_id: "fz_expense_exp123" + reseller_expense_id: "reseller_exp_456" + fzc_id: "fz_company_xyz789" + amount: 500.00 + currency: "USD" + category: "office_supplies" + description: "Office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123.pdf" + date: "2024-01-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-15T10:30:00Z" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + put: + tags: + - Expenses + summary: Update expense + description: Update an existing expense using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_expense_id + in: path + required: true + description: Freezone-issued expense ID + schema: + type: string + example: "fz_expense_exp123" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateFreeZoneExpenseRequest' + example: + amount: 550.00 + currency: "USD" + category: "office_supplies" + description: "Updated office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123_updated.pdf" + date: "2024-01-15" + responses: + '200': + description: Expense updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/FreeZoneExpense' + example: + fz_expense_id: "fz_expense_exp123" + reseller_expense_id: "reseller_exp_456" + fzc_id: "fz_company_xyz789" + amount: 550.00 + currency: "USD" + category: "office_supplies" + description: "Updated office equipment purchase" + vendor: "Office Depot" + receipt_url: "https://receipts.example.com/receipt123_updated.pdf" + date: "2024-01-15" + created_at: "2024-01-15T10:30:00Z" + updated_at: "2024-01-16T14:45:00Z" + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + + delete: + tags: + - Expenses + summary: Delete expense + description: Delete an expense using Freezone-issued IDs + parameters: + - name: fzc_id + in: path + required: true + description: Freezone-issued company ID + schema: + type: string + example: "fz_company_xyz789" + - name: fz_expense_id + in: path + required: true + description: Freezone-issued expense ID + schema: + type: string + example: "fz_expense_exp123" + responses: + '204': + description: Expense deleted successfully + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key + description: API key for authentication + + schemas: + # Digital Resident schemas + DigitalResident: + type: object + required: + - resident_id + - reseller_user_id + - email + - kyc_provider_id + - status + - created_at + - updated_at + properties: + resident_id: + type: string + description: Freezone-issued resident ID + example: "fz_resident_abc123" + reseller_user_id: + type: string + description: Reseller's user ID (provided during creation) + example: "reseller_user_456" + email: + type: string + format: email + description: Digital resident email address + example: "john.doe@example.com" + first_name: + type: string + description: Digital resident first name + example: "John" + last_name: + type: string + description: Digital resident last name + example: "Doe" + kyc_provider_id: + type: string + description: ID used to query the KYC provider for resident's KYC information + example: "kyc_john_doe_789" + phone: + type: string + description: Digital resident phone number + example: "+1234567890" + status: + type: string + enum: [active, inactive, pending, suspended] + description: Digital resident status + example: "active" + created_at: + type: string + format: date-time + description: Digital resident creation timestamp + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + description: Digital resident last update timestamp + example: "2024-01-15T10:30:00Z" + + CreateDigitalResidentRequest: + type: object + required: + - reseller_user_id + - email + - first_name + - last_name + - kyc_provider_id + properties: + reseller_user_id: + type: string + description: Reseller's user ID + example: "reseller_user_456" + email: + type: string + format: email + description: Digital resident email address + example: "john.doe@example.com" + first_name: + type: string + description: Digital resident first name + example: "John" + last_name: + type: string + description: Digital resident last name + example: "Doe" + kyc_provider_id: + type: string + description: ID used to query the KYC provider for resident's KYC information + example: "kyc_john_doe_789" + phone: + type: string + description: Digital resident phone number + example: "+1234567890" + + UpdateDigitalResidentRequest: + type: object + properties: + email: + type: string + format: email + description: Digital resident email address + example: "john.doe.updated@example.com" + first_name: + type: string + description: Digital resident first name + example: "John" + last_name: + type: string + description: Digital resident last name + example: "Doe" + kyc_provider_id: + type: string + description: ID used to query the KYC provider for resident's KYC information + example: "kyc_john_doe_updated_789" + phone: + type: string + description: Digital resident phone number + example: "+1234567891" + status: + type: string + enum: [active, inactive, pending, suspended] + description: Digital resident status + example: "active" + + # Free Zone Company schemas + FreeZoneCompany: + type: object + required: + - fzc_id + - reseller_company_id + - name + - type + - status + - shareholder_resident_ids + - crypto_wallets + - created_at + - updated_at + properties: + fzc_id: + type: string + description: Freezone-issued company ID + example: "fz_company_xyz789" + reseller_company_id: + type: string + description: Reseller's company ID (provided during creation) + example: "reseller_comp_123" + name: + type: string + description: Free zone company name + example: "Tech Innovations FZC" + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Free zone company type + example: "company" + description: + type: string + description: Free zone company description + example: "Technology consulting company" + registration_number: + type: string + description: Free zone company registration number + example: "FZC-2024-001" + tax_id: + type: string + description: Free zone company tax identification number + example: "TAX123456789" + address: + $ref: '#/components/schemas/Address' + shareholder_resident_ids: + type: array + items: + type: string + description: Array of Freezone-issued resident IDs who are shareholders + example: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + status: + type: string + enum: [active, inactive, pending, suspended] + description: Free zone company status + example: "active" + created_at: + type: string + format: date-time + description: Free zone company creation timestamp + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + description: Free zone company last update timestamp + example: "2024-01-15T10:30:00Z" + + CreateFreeZoneCompanyRequest: + type: object + required: + - reseller_company_id + - name + - type + - shareholder_resident_ids + properties: + reseller_company_id: + type: string + description: Reseller's company ID + example: "reseller_comp_123" + name: + type: string + description: Free zone company name + example: "Tech Innovations FZC" + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Free zone company type + example: "company" + description: + type: string + description: Free zone company description + example: "Technology consulting company" + registration_number: + type: string + description: Free zone company registration number + example: "FZC-2024-001" + tax_id: + type: string + description: Free zone company tax identification number + example: "TAX123456789" + address: + $ref: '#/components/schemas/Address' + shareholder_resident_ids: + type: array + items: + type: string + description: Array of Freezone-issued resident IDs who are shareholders + example: ["fz_resident_abc123", "fz_resident_def456"] + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + + UpdateFreeZoneCompanyRequest: + type: object + properties: + name: + type: string + description: Free zone company name + example: "Tech Innovations FZC Updated" + type: + type: string + enum: [company, cooperative, llc, corporation] + description: Free zone company type + example: "company" + description: + type: string + description: Free zone company description + example: "Updated technology consulting company" + registration_number: + type: string + description: Free zone company registration number + example: "FZC-2024-001" + tax_id: + type: string + description: Free zone company tax identification number + example: "TAX123456789" + address: + $ref: '#/components/schemas/Address' + shareholder_resident_ids: + type: array + items: + type: string + description: Array of Freezone-issued resident IDs who are shareholders + example: ["fz_resident_abc123", "fz_resident_def456", "fz_resident_ghi789"] + crypto_wallets: + type: array + items: + $ref: '#/components/schemas/CryptoWallet' + description: Associated crypto wallets for the company + status: + type: string + enum: [active, inactive, pending, suspended] + description: Free zone company status + example: "active" + + # Free Zone Invoice schemas + FreeZoneInvoice: + type: object + required: + - fz_invoice_id + - reseller_invoice_id + - fzc_id + - invoice_number + - amount + - currency + - status + - issue_date + - created_at + - updated_at + properties: + fz_invoice_id: + type: string + description: Freezone-issued invoice ID + example: "fz_invoice_inv123" + reseller_invoice_id: + type: string + description: Reseller's invoice ID (provided during creation) + example: "reseller_inv_456" + fzc_id: + type: string + description: Freezone-issued company ID this invoice belongs to + example: "fz_company_xyz789" + invoice_number: + type: string + description: Human-readable invoice number + example: "INV-2024-001" + amount: + type: number + format: decimal + description: Invoice amount + example: 1500.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + tax_amount: + type: number + format: decimal + description: Tax amount + example: 150.00 + description: + type: string + description: Invoice description + example: "Consulting services for Q1 2024" + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + status: + type: string + enum: [draft, sent, paid, overdue, cancelled] + description: Invoice status + example: "sent" + issue_date: + type: string + format: date + description: Invoice issue date + example: "2024-01-15" + due_date: + type: string + format: date + description: Invoice due date + example: "2024-02-15" + paid_date: + type: string + format: date + description: Invoice payment date + example: "2024-02-10" + created_at: + type: string + format: date-time + description: Invoice creation timestamp + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + description: Invoice last update timestamp + example: "2024-01-15T10:30:00Z" + + CreateFreeZoneInvoiceRequest: + type: object + required: + - reseller_invoice_id + - invoice_number + - amount + - currency + - issue_date + properties: + reseller_invoice_id: + type: string + description: Reseller's invoice ID + example: "reseller_inv_456" + invoice_number: + type: string + description: Human-readable invoice number + example: "INV-2024-001" + amount: + type: number + format: decimal + description: Invoice amount + example: 1500.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + tax_amount: + type: number + format: decimal + description: Tax amount + example: 150.00 + description: + type: string + description: Invoice description + example: "Consulting services for Q1 2024" + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + issue_date: + type: string + format: date + description: Invoice issue date + example: "2024-01-15" + due_date: + type: string + format: date + description: Invoice due date + example: "2024-02-15" + + UpdateFreeZoneInvoiceRequest: + type: object + properties: + invoice_number: + type: string + description: Human-readable invoice number + example: "INV-2024-001-UPDATED" + amount: + type: number + format: decimal + description: Invoice amount + example: 1650.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + tax_amount: + type: number + format: decimal + description: Tax amount + example: 165.00 + description: + type: string + description: Invoice description + example: "Updated consulting services for Q1 2024" + line_items: + type: array + items: + $ref: '#/components/schemas/InvoiceLineItem' + description: Invoice line items + status: + type: string + enum: [draft, sent, paid, overdue, cancelled] + description: Invoice status + example: "sent" + issue_date: + type: string + format: date + description: Invoice issue date + example: "2024-01-15" + due_date: + type: string + format: date + description: Invoice due date + example: "2024-02-15" + paid_date: + type: string + format: date + description: Invoice payment date + example: "2024-02-10" + + # Free Zone Expense schemas + FreeZoneExpense: + type: object + required: + - fz_expense_id + - reseller_expense_id + - fzc_id + - amount + - currency + - category + - date + - created_at + - updated_at + properties: + fz_expense_id: + type: string + description: Freezone-issued expense ID + example: "fz_expense_exp123" + reseller_expense_id: + type: string + description: Reseller's expense ID (provided during creation) + example: "reseller_exp_456" + fzc_id: + type: string + description: Freezone-issued company ID this expense belongs to + example: "fz_company_xyz789" + amount: + type: number + format: decimal + description: Expense amount + example: 500.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + category: + type: string + description: Expense category + example: "office_supplies" + description: + type: string + description: Expense description + example: "Office equipment purchase" + vendor: + type: string + description: Vendor name + example: "Office Depot" + receipt_url: + type: string + format: uri + description: URL to receipt document + example: "https://receipts.example.com/receipt123.pdf" + date: + type: string + format: date + description: Expense date + example: "2024-01-15" + created_at: + type: string + format: date-time + description: Expense creation timestamp + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + description: Expense last update timestamp + example: "2024-01-15T10:30:00Z" + + CreateFreeZoneExpenseRequest: + type: object + required: + - reseller_expense_id + - amount + - currency + - category + - date + properties: + reseller_expense_id: + type: string + description: Reseller's expense ID + example: "reseller_exp_456" + amount: + type: number + format: decimal + description: Expense amount + example: 500.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + category: + type: string + description: Expense category + example: "office_supplies" + description: + type: string + description: Expense description + example: "Office equipment purchase" + vendor: + type: string + description: Vendor name + example: "Office Depot" + receipt_url: + type: string + format: uri + description: URL to receipt document + example: "https://receipts.example.com/receipt123.pdf" + date: + type: string + format: date + description: Expense date + example: "2024-01-15" + + UpdateFreeZoneExpenseRequest: + type: object + properties: + amount: + type: number + format: decimal + description: Expense amount + example: 550.00 + currency: + type: string + description: Currency code (e.g., USD, EUR) + example: "USD" + category: + type: string + description: Expense category + example: "office_supplies" + description: + type: string + description: Expense description + example: "Updated office equipment purchase" + vendor: + type: string + description: Vendor name + example: "Office Depot" + receipt_url: + type: string + format: uri + description: URL to receipt document + example: "https://receipts.example.com/receipt123_updated.pdf" + date: + type: string + format: date + description: Expense date + example: "2024-01-15" + + # Supporting schemas + CryptoWallet: + type: object + required: + - public_key + - chain + properties: + public_key: + type: string + description: Wallet public key + example: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + chain: + type: string + enum: [bitcoin, ethereum, polygon, binance_smart_chain, solana] + description: Blockchain network + example: "bitcoin" + label: + type: string + description: Optional wallet label + example: "Main Bitcoin Wallet" + + Address: + type: object + properties: + street: + type: string + description: Street address + example: "123 Business Bay" + city: + type: string + description: City + example: "Dubai" + state: + type: string + description: State or province + example: "Dubai" + postal_code: + type: string + description: Postal code + example: "12345" + country: + type: string + description: Country code (ISO 3166-1 alpha-2) + example: "AE" + + InvoiceLineItem: + type: object + required: + - description + - quantity + - unit_price + - amount + properties: + description: + type: string + description: Line item description + example: "Strategy consulting" + quantity: + type: number + description: Quantity + example: 10 + unit_price: + type: number + format: decimal + description: Unit price + example: 150.00 + amount: + type: number + format: decimal + description: Total amount for this line item + example: 1500.00 + + + + Pagination: + type: object + required: + - page + - limit + - total + - total_pages + properties: + page: + type: integer + description: Current page number + example: 1 + limit: + type: integer + description: Number of items per page + example: 10 + total: + type: integer + description: Total number of items + example: 25 + total_pages: + type: integer + description: Total number of pages + example: 3 + + Error: + type: object + required: + - error + - message + properties: + error: + type: string + description: Error code + example: "bad_request" + message: + type: string + description: Human-readable error message + example: "Invalid request parameters" + details: + type: object + description: Additional error details + example: + field: "email" + issue: "Invalid email format" + + responses: + BadRequest: + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "bad_request" + message: "Invalid request parameters" + details: + field: "email" + issue: "Invalid email format" + + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "unauthorized" + message: "Invalid or missing API key" + + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "not_found" + message: "Resource not found" + + Conflict: + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "conflict" + message: "Resource already exists" + details: + field: "reseller_user_id" + value: "reseller_user_456" + + RateLimited: + description: Rate limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: "rate_limited" + message: "Rate limit exceeded. Try again later." + headers: + X-RateLimit-Limit: + description: Request limit per minute + schema: + type: integer + example: 100 + X-RateLimit-Remaining: + description: Remaining requests in current window + schema: + type: integer + example: 45 + X-RateLimit-Reset: + description: Time when rate limit resets (Unix timestamp) + schema: + type: integer + example: 1705320000 \ No newline at end of file diff --git a/src/api/package.json b/src/api/package.json new file mode 100644 index 0000000..aef70c9 --- /dev/null +++ b/src/api/package.json @@ -0,0 +1,30 @@ +{ + "name": "freezone-api-docs", + "version": "1.0.0", + "description": "Documentation and mock server for Freezone API", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "install-deps": "npm install", + "test-mock": "./test-mock-api.sh" + }, + "dependencies": { + "express": "^4.18.2", + "http-proxy-middleware": "^2.0.6", + "@stoplight/prism-cli": "^5.8.0" + }, + "devDependencies": { + "nodemon": "^3.0.2" + }, + "keywords": [ + "openapi", + "mock", + "api", + "freezone", + "swagger", + "documentation" + ], + "author": "Freezone API Team", + "license": "MIT" +} \ No newline at end of file diff --git a/src/api/serve.sh b/src/api/serve.sh new file mode 100755 index 0000000..18308ff --- /dev/null +++ b/src/api/serve.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Simple HTTP server to serve the Swagger UI and OpenAPI spec +# Usage: ./serve.sh [port] + +PORT=${1:-8080} +API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "๐Ÿš€ Starting Freezone API Documentation Server..." +echo "๐Ÿ“ Serving from: $API_DIR" +echo "๐ŸŒ URL: http://localhost:$PORT" +echo "๐Ÿ“– Swagger UI: http://localhost:$PORT/swagger-ui.html" +echo "" +echo "๐Ÿ“‹ Available files:" +echo " - swagger-ui.html (Interactive API documentation)" +echo " - openapi_updated.yaml (OpenAPI specification)" +echo " - README.md (API documentation)" +echo "" +echo "Press Ctrl+C to stop the server" +echo "----------------------------------------" + +# Check if Python 3 is available +if command -v python3 &> /dev/null; then + cd "$API_DIR" + python3 -m http.server $PORT +elif command -v python &> /dev/null; then + cd "$API_DIR" + python -m http.server $PORT +elif command -v node &> /dev/null; then + # Use Node.js if available + cd "$API_DIR" + npx http-server -p $PORT +else + echo "โŒ Error: No suitable HTTP server found." + echo "Please install one of the following:" + echo " - Python 3: python3 -m http.server" + echo " - Python 2: python -m SimpleHTTPServer" + echo " - Node.js: npx http-server" + exit 1 +fi \ No newline at end of file diff --git a/src/api/server.js b/src/api/server.js new file mode 100644 index 0000000..f54e760 --- /dev/null +++ b/src/api/server.js @@ -0,0 +1,175 @@ +const express = require('express'); +const path = require('path'); +const { createProxyMiddleware } = require('http-proxy-middleware'); +const { spawn } = require('child_process'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Serve static files (documentation) +app.use(express.static(__dirname)); + +// Start Prism mock server on a different port internally +let prismProcess; + +function startPrismServer() { + console.log('๐Ÿ”ง Starting internal mock server...'); + prismProcess = spawn('npx', ['prism', 'mock', 'openapi_updated.yaml', '--host', '127.0.0.1', '--port', '4001', '--dynamic'], { + cwd: __dirname, + stdio: ['pipe', 'pipe', 'pipe'] + }); + + prismProcess.stdout.on('data', (data) => { + const output = data.toString().trim(); + if (output && !output.includes('Prism is listening')) { + console.log(`[Internal] ${output}`); + } + }); + + prismProcess.stderr.on('data', (data) => { + const output = data.toString().trim(); + if (output) { + console.log(`[Internal] ${output}`); + } + }); + + prismProcess.on('close', (code) => { + console.log(`[Internal] Mock server process exited with code ${code}`); + }); + + // Give Prism time to start + setTimeout(() => { + console.log('โœ… Mock API service ready at /api/mock'); + }, 3000); +} + +// Proxy /api/mock/* to the internal Prism server +app.use('/api/mock', createProxyMiddleware({ + target: 'http://127.0.0.1:4001', + changeOrigin: true, + pathRewrite: { + '^/api/mock': '', // Remove /api/mock prefix when forwarding to Prism + }, + onError: (err, req, res) => { + console.error('Proxy error:', err.message); + res.status(503).json({ + error: 'mock_server_unavailable', + message: 'Mock API server is not available. Please wait a moment and try again.', + details: { + endpoint: req.originalUrl, + internal_error: err.message + } + }); + }, + onProxyReq: (proxyReq, req, res) => { + console.log(`[Mock API] ${req.method} ${req.originalUrl} -> http://127.0.0.1:4001${req.url}`); + } +})); + +// API status endpoint +app.get('/api/status', (req, res) => { + res.json({ + status: 'running', + services: { + documentation: { + status: 'active', + endpoints: [ + 'GET /', + 'GET /swagger-ui.html', + 'GET /openapi_updated.yaml', + 'GET /README.md' + ] + }, + mock_api: { + status: prismProcess ? 'active' : 'starting', + base_url: '/api/mock', + endpoints: [ + 'POST /api/mock/auth/api-key', + 'GET /api/mock/digital-residents', + 'POST /api/mock/digital-residents', + 'GET /api/mock/free-zone-companies', + 'POST /api/mock/free-zone-companies', + 'GET /api/mock/free-zone-companies/{fzc_id}/invoices', + 'GET /api/mock/free-zone-companies/{fzc_id}/expenses' + ] + } + }, + timestamp: new Date().toISOString() + }); +}); + +// Health check endpoint +app.get('/health', (req, res) => { + res.json({ status: 'healthy', timestamp: new Date().toISOString() }); +}); + +// Default route - redirect to Swagger UI +app.get('/', (req, res) => { + res.redirect('/swagger-ui.html'); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).json({ + error: 'not_found', + message: `Endpoint ${req.method} ${req.path} not found`, + available_endpoints: { + documentation: [ + 'GET /', + 'GET /swagger-ui.html', + 'GET /openapi_updated.yaml', + 'GET /README.md' + ], + mock_api: [ + 'POST /api/mock/auth/api-key', + 'GET /api/mock/digital-residents', + 'POST /api/mock/digital-residents', + 'GET /api/mock/free-zone-companies', + 'POST /api/mock/free-zone-companies' + ], + status: [ + 'GET /api/status', + 'GET /health' + ] + } + }); +}); + +// Graceful shutdown +process.on('SIGINT', () => { + console.log('\n๐Ÿ›‘ Shutting down servers...'); + if (prismProcess) { + prismProcess.kill(); + } + process.exit(0); +}); + +// Start the server +app.listen(PORT, () => { + console.log('๐Ÿš€ Freezone API Documentation & Mock Server'); + console.log('=========================================='); + console.log(`๐ŸŒ Server running on: http://localhost:${PORT}`); + console.log(''); + console.log('๐Ÿ“‹ Available Services:'); + console.log(` ๐Ÿ“– Documentation: http://localhost:${PORT}/swagger-ui.html`); + console.log(` ๐Ÿ”ง Mock API: http://localhost:${PORT}/api/mock`); + console.log(` ๐Ÿ“„ OpenAPI Spec: http://localhost:${PORT}/openapi_updated.yaml`); + console.log(` ๐Ÿ“Š Status: http://localhost:${PORT}/api/status`); + console.log(''); + console.log('๐Ÿ”ง Mock API Examples:'); + console.log(` POST http://localhost:${PORT}/api/mock/auth/api-key`); + console.log(` GET http://localhost:${PORT}/api/mock/digital-residents`); + console.log(` POST http://localhost:${PORT}/api/mock/digital-residents`); + console.log(` GET http://localhost:${PORT}/api/mock/free-zone-companies`); + console.log(''); + console.log('๐Ÿ’ก Tips:'); + console.log(' - Mock API returns example responses from OpenAPI spec'); + console.log(' - Use X-API-Key header for authenticated endpoints'); + console.log(' - All services run on the same port for convenience'); + console.log(''); + console.log('Press Ctrl+C to stop the server'); + console.log('=========================================='); + + // Start Prism after Express server is running + startPrismServer(); +}); \ No newline at end of file diff --git a/src/api/start-mock-server.sh b/src/api/start-mock-server.sh new file mode 100755 index 0000000..8d17c9a --- /dev/null +++ b/src/api/start-mock-server.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Start both documentation and mock API server +# Usage: ./start-mock-server.sh + +API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$API_DIR" + +echo "๐Ÿš€ Starting Freezone API Mock Server & Documentation..." +echo "" +echo "๐Ÿ“‹ Services:" +echo " ๐Ÿ“– Documentation: http://localhost:3000/swagger-ui.html" +echo " ๐Ÿ”ง Mock API: http://localhost:4000" +echo " ๐Ÿ“„ OpenAPI Spec: http://localhost:3000/openapi_updated.yaml" +echo "" +echo "๐Ÿ”ง Mock API Endpoints:" +echo " POST http://localhost:4000/auth/api-key" +echo " GET http://localhost:4000/digital-residents" +echo " POST http://localhost:4000/digital-residents" +echo " GET http://localhost:4000/free-zone-companies" +echo " POST http://localhost:4000/free-zone-companies" +echo " GET http://localhost:4000/free-zone-companies/{fzc_id}/invoices" +echo " GET http://localhost:4000/free-zone-companies/{fzc_id}/expenses" +echo "" +echo "๐Ÿ’ก Tips:" +echo " - Mock API returns example responses from OpenAPI spec" +echo " - Use 'X-API-Key: test-key' header for authenticated endpoints" +echo " - Dynamic responses include realistic data variations" +echo "" +echo "Press Ctrl+C to stop all servers" +echo "----------------------------------------" + +# Check if Node.js and npm are available +if ! command -v node &> /dev/null; then + echo "โŒ Error: Node.js is required but not installed." + echo "Please install Node.js from https://nodejs.org/" + exit 1 +fi + +if ! command -v npm &> /dev/null; then + echo "โŒ Error: npm is required but not installed." + echo "Please install npm (usually comes with Node.js)" + exit 1 +fi + +# Check if Prism is installed globally +if ! command -v prism &> /dev/null; then + echo "๐Ÿ“ฆ Installing Prism CLI globally..." + npm install -g @stoplight/prism-cli + if [ $? -ne 0 ]; then + echo "โŒ Failed to install Prism CLI. Trying local installation..." + npm install @stoplight/prism-cli + PRISM_CMD="npx prism" + else + PRISM_CMD="prism" + fi +else + PRISM_CMD="prism" +fi + +# Check if concurrently is available +if ! command -v concurrently &> /dev/null; then + echo "๐Ÿ“ฆ Installing concurrently..." + npm install concurrently + CONCURRENTLY_CMD="npx concurrently" +else + CONCURRENTLY_CMD="concurrently" +fi + +# Start both servers concurrently +$CONCURRENTLY_CMD \ + --names "DOCS,MOCK" \ + --prefix-colors "blue,green" \ + "python3 -m http.server 3000" \ + "$PRISM_CMD mock openapi_updated.yaml --host 0.0.0.0 --port 4000 --dynamic" \ No newline at end of file diff --git a/src/api/swagger-ui.html b/src/api/swagger-ui.html new file mode 100644 index 0000000..ca3623c --- /dev/null +++ b/src/api/swagger-ui.html @@ -0,0 +1,153 @@ + + + + + + Freezone API Documentation + + + + +
+ + + + + + \ No newline at end of file diff --git a/src/api/test-mock-api.sh b/src/api/test-mock-api.sh new file mode 100755 index 0000000..197a7d3 --- /dev/null +++ b/src/api/test-mock-api.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Test script for the mock API server +# Usage: ./test-mock-api.sh + +BASE_URL="http://localhost:3000/api/mock" +API_KEY="test-api-key" + +echo "๐Ÿงช Testing Freezone Mock API Server" +echo "๐ŸŒ Base URL: $BASE_URL" +echo "๐Ÿ”‘ API Key: $API_KEY" +echo "----------------------------------------" + +# Function to make API calls with proper formatting +test_endpoint() { + local method=$1 + local endpoint=$2 + local description=$3 + local data=$4 + + echo "" + echo "๐Ÿ“‹ Testing: $description" + echo "๐Ÿ”— $method $endpoint" + + if [ -n "$data" ]; then + echo "๐Ÿ“ค Request Body:" + echo "$data" | jq '.' 2>/dev/null || echo "$data" + echo "" + response=$(curl -s -X "$method" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d "$data" \ + "$BASE_URL$endpoint") + else + response=$(curl -s -X "$method" \ + -H "X-API-Key: $API_KEY" \ + "$BASE_URL$endpoint") + fi + + echo "๐Ÿ“ฅ Response:" + echo "$response" | jq '.' 2>/dev/null || echo "$response" + echo "----------------------------------------" +} + +# Check if jq is available for JSON formatting +if ! command -v jq &> /dev/null; then + echo "๐Ÿ’ก Tip: Install 'jq' for better JSON formatting: brew install jq" + echo "" +fi + +# Check if mock server is running +echo "๐Ÿ” Checking if mock server is running..." +if ! curl -s "$BASE_URL" > /dev/null; then + echo "โŒ Mock server is not running on $BASE_URL" + echo "๐Ÿ’ก Start it with: ./start-mock-server.sh" + exit 1 +fi +echo "โœ… Mock server is running!" + +# Test Authentication +test_endpoint "POST" "/auth/api-key" "Generate API Key" '{ + "client_id": "reseller_123", + "client_secret": "secret_abc123xyz", + "access_password": "freezone_access_2024" +}' + +# Test Digital Residents +test_endpoint "GET" "/digital-residents" "List Digital Residents" + +test_endpoint "POST" "/digital-residents" "Create Digital Resident" '{ + "reseller_user_id": "reseller_user_456", + "email": "john.doe@example.com", + "first_name": "John", + "last_name": "Doe", + "kyc_provider_id": "kyc_john_doe_789", + "phone": "+1234567890" +}' + +test_endpoint "GET" "/digital-residents/fz_resident_abc123" "Get Digital Resident" + +# Test Free Zone Companies +test_endpoint "GET" "/free-zone-companies" "List Free Zone Companies" + +test_endpoint "POST" "/free-zone-companies" "Create Free Zone Company" '{ + "reseller_company_id": "reseller_comp_123", + "name": "Tech Innovations FZC", + "type": "company", + "description": "Technology consulting company", + "registration_number": "FZC-2024-001", + "tax_id": "TAX123456789", + "address": { + "street": "123 Business Bay", + "city": "Dubai", + "state": "Dubai", + "postal_code": "12345", + "country": "AE" + }, + "shareholder_resident_ids": ["fz_resident_abc123", "fz_resident_def456"], + "crypto_wallets": [ + { + "public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", + "chain": "bitcoin", + "label": "Main Bitcoin Wallet" + } + ] +}' + +test_endpoint "GET" "/free-zone-companies/fz_company_xyz789" "Get Free Zone Company" + +# Test Invoices +test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices" "List Company Invoices" + +test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/invoices" "Create Invoice" '{ + "reseller_invoice_id": "reseller_inv_456", + "invoice_number": "INV-2024-001", + "amount": 1500.00, + "currency": "USD", + "tax_amount": 150.00, + "description": "Consulting services for Q1 2024", + "line_items": [ + { + "description": "Strategy consulting", + "quantity": 10, + "unit_price": 150.00, + "amount": 1500.00 + } + ], + "issue_date": "2024-01-15", + "due_date": "2024-02-15" +}' + +test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices/fz_invoice_inv123" "Get Invoice" + +# Test Expenses +test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses" "List Company Expenses" + +test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/expenses" "Create Expense" '{ + "reseller_expense_id": "reseller_exp_456", + "amount": 500.00, + "currency": "USD", + "category": "office_supplies", + "description": "Office equipment purchase", + "vendor": "Office Depot", + "receipt_url": "https://receipts.example.com/receipt123.pdf", + "date": "2024-01-15" +}' + +test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses/fz_expense_exp123" "Get Expense" + +echo "" +echo "โœ… Mock API testing completed!" +echo "๐Ÿ’ก All endpoints should return example responses from the OpenAPI specification" +echo "๐Ÿ”ง Mock server provides realistic data for development and testing" \ No newline at end of file