openapi: 3.0.3 info: title: 'Dokio API Documentation' description: 'REST API for the Dokio multi-vendor marketplace platform.' version: 1.0.0 servers: - url: 'https://dokio.shop' tags: - name: Authentication description: 'Register, log in, verify email, and manage your session.' - name: Users description: 'Retrieve user accounts and related store data.' - name: Stores description: 'Create and manage vendor stores.' - name: Products description: 'Browse and manage marketplace products.' - name: Categories description: 'Manage product categories.' - name: Enums description: 'Lookup values used by other endpoints (e.g. store industries).' - name: Checkout description: 'Guest COD checkout.' - name: Endpoints description: '' - name: Orders description: 'Vendor order management.' - name: 'Product Options' description: 'Manage product options.' - name: 'Product Variants' description: 'Manage product variants.' - name: 'Storefront rows' description: 'Public homepage sections for a store.' - name: 'Storefront slides' description: 'Manage slides for homepage slider sections.' components: securitySchemes: default: type: http scheme: bearer description: " Obtain a token by calling **POST /api/login** or **POST /api/register**.\n Tokens are Laravel Sanctum personal access tokens and expire after one month.\n Pass the token as: `Authorization: Bearer {token}`.\n Email verification is required before login succeeds." security: - default: [] paths: /api/me: get: summary: 'Current user' operationId: currentUser description: 'Return the authenticated user, including their store when available.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'User fetched successfully' data: type: user id: '1' attributes: name: 'Jane Doe' email: jane@example.com relationships: store: data: id: '1' type: store links: self: 'https://api.dokio.local/api/v1/stores/1' properties: status: type: integer example: 200 message: type: string example: 'User fetched successfully' data: type: object properties: type: type: string example: user id: type: string example: '1' attributes: type: object properties: name: type: string example: 'Jane Doe' email: type: string example: jane@example.com relationships: type: object properties: store: type: object properties: data: type: object properties: id: type: string example: '1' type: type: string example: store links: type: object properties: self: type: string example: 'https://api.dokio.local/api/v1/stores/1' tags: - Authentication /api/login: post: summary: 'Log in' operationId: logIn description: 'Authenticate with email and password. Returns a Sanctum token valid for one month. The account must be email-verified.' parameters: [] responses: 200: description: 'Successful login' content: application/json: schema: type: object example: status: 200 message: 'Logged in successfully' data: token: 1|abcXYZ... verified: true properties: status: type: integer example: 200 message: type: string example: 'Logged in successfully' data: type: object properties: token: type: string example: 1|abcXYZ... verified: type: boolean example: true 400: description: 'Email not verified' content: application/json: schema: type: object example: errors: - status: '400' code: email.not_verified detail: 'Email not verified' properties: errors: type: array example: - status: '400' code: email.not_verified detail: 'Email not verified' items: type: object properties: status: type: string example: '400' code: type: string example: email.not_verified detail: type: string example: 'Email not verified' 401: description: 'Invalid credentials' content: application/json: schema: type: object example: errors: - status: '401' code: credentials.invalid detail: 'Invalid credentials' properties: errors: type: array example: - status: '401' code: credentials.invalid detail: 'Invalid credentials' items: type: object properties: status: type: string example: '401' code: type: string example: credentials.invalid detail: type: string example: 'Invalid credentials' tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'The user email address. Must be a valid email address.' example: jane@example.com password: type: string description: 'The account password (minimum 8 characters). Must be at least 8 characters.' example: password123 required: - email - password security: [] /api/register: post: summary: Register operationId: register description: 'Create a new user account and receive a Sanctum token. A verification email is sent automatically.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'User registered successfully' data: token: 1|abcXYZ... verified: false properties: status: type: integer example: 201 message: type: string example: 'User registered successfully' data: type: object properties: token: type: string example: 1|abcXYZ... verified: type: boolean example: false tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The user full name. Must not be greater than 255 characters.' example: 'Jane Doe' email: type: string description: 'A unique email address for the account. Must be a valid email address.' example: jane@example.com password: type: string description: 'The account password (minimum 8 characters). Must be at least 8 characters.' example: password123 required: - name - email - password security: [] /api/auth/google: post: summary: 'Register or log in with Google' operationId: registerOrLogInWithGoogle description: 'Authenticate with a Google ID token from the client. Creates an account if needed, or links Google to an existing email account. Returns a Sanctum token valid for one month.' parameters: [] responses: 200: description: 'Successful Google authentication' content: application/json: schema: type: object example: status: 200 message: 'Logged in successfully' data: token: 1|abcXYZ... verified: true properties: status: type: integer example: 200 message: type: string example: 'Logged in successfully' data: type: object properties: token: type: string example: 1|abcXYZ... verified: type: boolean example: true 400: description: 'Google account email is not verified' content: application/json: schema: type: object example: errors: - status: '400' code: google.email_not_verified detail: 'Google email not verified' properties: errors: type: array example: - status: '400' code: google.email_not_verified detail: 'Google email not verified' items: type: object properties: status: type: string example: '400' code: type: string example: google.email_not_verified detail: type: string example: 'Google email not verified' 401: description: 'Invalid or expired ID token' content: application/json: schema: type: object example: errors: - status: '401' code: google.invalid_id_token detail: 'Invalid Google token' properties: errors: type: array example: - status: '401' code: google.invalid_id_token detail: 'Invalid Google token' items: type: object properties: status: type: string example: '401' code: type: string example: google.invalid_id_token detail: type: string example: 'Invalid Google token' tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: id_token: type: string description: 'The Google ID token obtained from Google Sign-In on the client.' example: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... required: - id_token security: [] /api/auth/google/code: post: summary: 'Register or log in with a Google authorization code' operationId: registerOrLogInWithAGoogleAuthorizationCode description: 'Exchange a one-time Google popup code for an ID token, then authenticate the user. The request Origin must match FRONTEND_URL and X-Requested-With must be XMLHttpRequest. Returns a Sanctum token valid for one month.' parameters: [] responses: 200: description: 'Successful Google authentication' content: application/json: schema: type: object example: status: 200 message: 'Logged in successfully' data: token: 1|abcXYZ... verified: true properties: status: type: integer example: 200 message: type: string example: 'Logged in successfully' data: type: object properties: token: type: string example: 1|abcXYZ... verified: type: boolean example: true 401: description: 'Invalid or expired authorization code' content: application/json: schema: type: object example: errors: - status: '401' code: google.invalid_code detail: 'Invalid Google authorization code' properties: errors: type: array example: - status: '401' code: google.invalid_code detail: 'Invalid Google authorization code' items: type: object properties: status: type: string example: '401' code: type: string example: google.invalid_code detail: type: string example: 'Invalid Google authorization code' 403: description: 'Request origin is not the configured dashboard origin' content: application/json: schema: type: object example: errors: - status: '403' code: google.invalid_origin detail: 'Invalid Google sign-in origin' properties: errors: type: array example: - status: '403' code: google.invalid_origin detail: 'Invalid Google sign-in origin' items: type: object properties: status: type: string example: '403' code: type: string example: google.invalid_origin detail: type: string example: 'Invalid Google sign-in origin' tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: code: type: string description: 'The one-time authorization code returned by the Google popup.' example: synthetic-google-authorization-code required: - code security: [] /api/forgot-password: post: summary: 'Forgot password' operationId: forgotPassword description: 'Send a password reset email if the account exists.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Password reset email sent' data: [] properties: status: type: integer example: 200 message: type: string example: 'Password reset email sent' data: type: array example: [] 404: description: '' content: application/json: schema: type: object example: errors: - status: '404' code: user.not_found detail: 'User not found' properties: errors: type: array example: - status: '404' code: user.not_found detail: 'User not found' items: type: object properties: status: type: string example: '404' code: type: string example: user.not_found detail: type: string example: 'User not found' tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'The email address of the account to reset. Must be a valid email address.' example: jane@example.com required: - email security: [] /api/reset-password: post: summary: 'Reset password' operationId: resetPassword description: 'Set a new password using the token from the reset email. All existing tokens are revoked.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Password reset successfully' data: [] properties: status: type: integer example: 200 message: type: string example: 'Password reset successfully' data: type: array example: [] 400: description: '' content: application/json: schema: type: object example: errors: - status: '400' code: passwords.reset_failed detail: 'Failed to reset password' properties: errors: type: array example: - status: '400' code: passwords.reset_failed detail: 'Failed to reset password' items: type: object properties: status: type: string example: '400' code: type: string example: passwords.reset_failed detail: type: string example: 'Failed to reset password' tags: - Authentication requestBody: required: true content: application/json: schema: type: object properties: token: type: string description: 'The password reset token from the email link.' example: abc123resetToken email: type: string description: 'The email address of the account being reset. Must be a valid email address.' example: jane@example.com password: type: string description: 'The new password (minimum 8 characters). Must be at least 8 characters.' example: newpassword123 required: - token - email - password security: [] /api/logout: post: summary: 'Log out' operationId: logOut description: 'Revoke the current access token.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Logged out successfully' data: [] properties: status: type: integer example: 200 message: type: string example: 'Logged out successfully' data: type: array example: [] tags: - Authentication '/api/email/verify/{id}/{hash}': get: summary: 'Verify email' operationId: verifyEmail description: 'Confirm the user email address using the link from the verification email.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'Email verified successfully' data: token: 1|abcXYZ... verified: true properties: status: type: integer example: 201 message: type: string example: 'Email verified successfully' data: type: object properties: token: type: string example: 1|abcXYZ... verified: type: boolean example: true 400: description: '' content: application/json: schema: oneOf: - description: '' type: object example: errors: - status: '400' code: verification.invalid_link detail: 'Invalid verification link' properties: errors: type: array example: - status: '400' code: verification.invalid_link detail: 'Invalid verification link' items: type: object properties: status: type: string example: '400' code: type: string example: verification.invalid_link detail: type: string example: 'Invalid verification link' - description: '' type: object example: errors: - status: '400' code: verification.email_already_verified detail: 'Email already verified' properties: errors: type: array example: - status: '400' code: verification.email_already_verified detail: 'Email already verified' items: type: object properties: status: type: string example: '400' code: type: string example: verification.email_already_verified detail: type: string example: 'Email already verified' tags: - Authentication security: [] parameters: - in: path name: id description: 'The user ID.' example: 1 required: true schema: type: integer - in: path name: hash description: 'SHA1 hash of the user email (from the verification link).' example: a1b2c3d4e5f6 required: true schema: type: string /api/v1/users: get: summary: 'List users' operationId: listUsers description: 'Return a paginated list of users with their stores eager-loaded.' parameters: - in: query name: page description: 'Page number for pagination.' example: 1 required: false schema: type: integer description: 'Page number for pagination.' example: 1 responses: 200: description: '' content: application/json: schema: type: object example: data: - type: user id: '51' attributes: name: 'Morgan Hirthe' email: dare.emelie@example.com relationships: store: data: id: '47' type: store links: self: 'https://dokio.shop/api/v1/users?51' included: type: store id: 47 attributes: name: 'Prof. Mina Bauch' slug: prof-mina-bauch industry: fashion logo: 'https://dokio.shop/storage/https://via.placeholder.com/640x480.png/002222?text=nostrum' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 51 type: user links: self: 'https://dokio.shop/api/v1/users/47' links: self: 'https://dokio.shop/api/v1/users/47' links: self: 'https://dokio.shop/api/v1/users/51' - type: user id: '52' attributes: name: 'Carey Smitham' email: sleffler@example.org relationships: store: data: id: '48' type: store links: self: 'https://dokio.shop/api/v1/users?52' included: type: store id: 48 attributes: name: 'Matilda Feeney' slug: matilda-feeney industry: food logo: 'https://dokio.shop/storage/https://via.placeholder.com/640x480.png/0088cc?text=laboriosam' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 52 type: user links: self: 'https://dokio.shop/api/v1/users/48' links: self: 'https://dokio.shop/api/v1/users/48' links: self: 'https://dokio.shop/api/v1/users/52' links: first: '/?page=1' last: '/?page=1' prev: null next: null meta: current_page: 1 from: 1 last_page: 1 links: - url: null label: '« Previous' page: null active: false - url: '/?page=1' label: '1' page: 1 active: true - url: null label: 'Next »' page: null active: false path: / per_page: 15 to: 2 total: 2 properties: data: type: array example: - type: user id: '51' attributes: name: 'Morgan Hirthe' email: dare.emelie@example.com relationships: store: data: id: '47' type: store links: self: 'https://dokio.shop/api/v1/users?51' included: type: store id: 47 attributes: name: 'Prof. Mina Bauch' slug: prof-mina-bauch industry: fashion logo: 'https://dokio.shop/storage/https://via.placeholder.com/640x480.png/002222?text=nostrum' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 51 type: user links: self: 'https://dokio.shop/api/v1/users/47' links: self: 'https://dokio.shop/api/v1/users/47' links: self: 'https://dokio.shop/api/v1/users/51' - type: user id: '52' attributes: name: 'Carey Smitham' email: sleffler@example.org relationships: store: data: id: '48' type: store links: self: 'https://dokio.shop/api/v1/users?52' included: type: store id: 48 attributes: name: 'Matilda Feeney' slug: matilda-feeney industry: food logo: 'https://dokio.shop/storage/https://via.placeholder.com/640x480.png/0088cc?text=laboriosam' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 52 type: user links: self: 'https://dokio.shop/api/v1/users/48' links: self: 'https://dokio.shop/api/v1/users/48' links: self: 'https://dokio.shop/api/v1/users/52' items: type: object properties: type: type: string example: user id: type: string example: '51' attributes: type: object properties: name: type: string example: 'Morgan Hirthe' email: type: string example: dare.emelie@example.com relationships: type: object properties: store: type: object properties: data: type: object properties: id: type: string example: '47' type: type: string example: store links: type: object properties: self: type: string example: 'https://dokio.shop/api/v1/users?51' included: type: object properties: type: type: string example: store id: type: integer example: 47 attributes: type: object properties: name: type: string example: 'Prof. Mina Bauch' slug: type: string example: prof-mina-bauch industry: type: string example: fashion logo: type: string example: 'https://dokio.shop/storage/https://via.placeholder.com/640x480.png/002222?text=nostrum' color: type: string example: '#007bff' createdAt: type: string example: '2026-09-21T17:28:48.000000Z' updatedAt: type: string example: '2026-09-21T17:28:48.000000Z' relationships: type: object properties: user: type: object properties: data: type: object properties: { id: { type: integer, example: 51 }, type: { type: string, example: user } } links: type: object properties: { self: { type: string, example: 'https://dokio.shop/api/v1/users/47' } } links: type: object properties: self: type: string example: 'https://dokio.shop/api/v1/users/47' links: type: object properties: self: type: string example: 'https://dokio.shop/api/v1/users/51' links: type: object properties: first: type: string example: '/?page=1' last: type: string example: '/?page=1' prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: current_page: type: integer example: 1 from: type: integer example: 1 last_page: type: integer example: 1 links: type: array example: - url: null label: '« Previous' page: null active: false - url: '/?page=1' label: '1' page: 1 active: true - url: null label: 'Next »' page: null active: false items: type: object properties: url: type: string example: null nullable: true label: type: string example: '« Previous' page: type: string example: null nullable: true active: type: boolean example: false path: type: string example: / per_page: type: integer example: 15 to: type: integer example: 2 total: type: integer example: 2 tags: - Users '/api/v1/users/{id}': get: summary: 'Get user' operationId: getUser description: 'Return a single user by ID, including their store.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: type: user id: '53' attributes: name: 'Morgan Hirthe' email: imclaughlin@example.org createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' emailVerifiedAt: '2026-09-21T17:28:48.000000Z' relationships: store: data: id: '49' type: store links: self: 'http://dokio.shop/api/v1/users?53' included: type: store id: 49 attributes: name: 'Mr. Oswald Koch' slug: mr-oswald-koch industry: home logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/00ee77?text=consequatur' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 53 type: user links: self: 'http://dokio.shop/api/v1/users/49' links: self: 'http://dokio.shop/api/v1/users/49' links: self: 'http://dokio.shop/api/v1/users/53' properties: data: type: object properties: type: type: string example: user id: type: string example: '53' attributes: type: object properties: name: type: string example: 'Morgan Hirthe' email: type: string example: imclaughlin@example.org createdAt: type: string example: '2026-09-21T17:28:48.000000Z' updatedAt: type: string example: '2026-09-21T17:28:48.000000Z' emailVerifiedAt: type: string example: '2026-09-21T17:28:48.000000Z' relationships: type: object properties: store: type: object properties: data: type: object properties: id: type: string example: '49' type: type: string example: store links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users?53' included: type: object properties: type: type: string example: store id: type: integer example: 49 attributes: type: object properties: name: type: string example: 'Mr. Oswald Koch' slug: type: string example: mr-oswald-koch industry: type: string example: home logo: type: string example: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/00ee77?text=consequatur' color: type: string example: '#007bff' createdAt: type: string example: '2026-09-21T17:28:48.000000Z' updatedAt: type: string example: '2026-09-21T17:28:48.000000Z' relationships: type: object properties: user: type: object properties: data: type: object properties: id: { type: integer, example: 53 } type: { type: string, example: user } links: type: object properties: self: { type: string, example: 'http://dokio.shop/api/v1/users/49' } links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/49' links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/53' tags: - Users parameters: - in: path name: id description: 'The ID of the user.' example: 1 required: true schema: type: integer - in: path name: user description: 'The user ID.' example: 1 required: true schema: type: integer /api/v1/stores: post: summary: 'Create store' operationId: createStore description: 'Create a store for the authenticated user. Accepts `multipart/form-data` when uploading a logo.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'Store created successfully' data: type: store id: 1 attributes: name: 'Acme Fashion' slug: acme-fashion industry: fashion logo: null color: '#007bff' properties: status: type: integer example: 201 message: type: string example: 'Store created successfully' data: type: object properties: type: type: string example: store id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Acme Fashion' slug: type: string example: acme-fashion industry: type: string example: fashion logo: type: string example: null nullable: true color: type: string example: '#007bff' tags: - Stores requestBody: required: true content: multipart/form-data: schema: type: object properties: name: type: string description: 'The store display name. Must not be greater than 255 characters.' example: 'Acme Fashion' industry: type: string description: 'Store industry. Allowed: fashion, electronics, food, beauty, home, other.' example: fashion enum: - fashion - electronics - food - beauty - home - other logo: type: string format: binary description: 'Optional store logo (jpeg, png, jpg, gif, or svg; max 2MB). Raster logos have whitespace trimmed with GD. SVG uploads are sanitized and their viewBox is tightened around artwork (full-canvas backgrounds ignored); they remain SVG. Must be an image. Must not be greater than 2048 kilobytes.' color: type: string description: 'Must not be greater than 255 characters.' example: b required: - name - industry - color '/api/v1/stores/{slug}': put: summary: 'Update store' operationId: updateStore description: 'Update an existing store. Accepts `multipart/form-data` when uploading a new logo.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Store updated successfully' data: type: store id: 1 attributes: name: 'Acme Fashion' slug: acme-fashion industry: fashion color: '#007bff' properties: status: type: integer example: 200 message: type: string example: 'Store updated successfully' data: type: object properties: type: type: string example: store id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Acme Fashion' slug: type: string example: acme-fashion industry: type: string example: fashion color: type: string example: '#007bff' tags: - Stores requestBody: required: true content: multipart/form-data: schema: type: object properties: name: type: string description: 'The store display name. Must not be greater than 255 characters.' example: 'Acme Fashion' industry: type: string description: 'Store industry. Allowed: fashion, electronics, food, beauty, home, other.' example: fashion enum: - fashion - electronics - food - beauty - home - other logo: type: string format: binary description: 'Optional new store logo (jpeg, png, jpg, gif, or svg; max 2MB). Replaces the existing logo. Raster logos have whitespace trimmed with GD. SVG uploads are sanitized and their viewBox is tightened around artwork (full-canvas backgrounds ignored); they remain SVG. Must be an image. Must not be greater than 2048 kilobytes.' color: type: string description: 'Must not be greater than 255 characters.' example: b required: - name - industry - color delete: summary: 'Delete store' operationId: deleteStore description: 'Remove a store when it has no related catalog data or orders.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Store deleted successfully' data: type: store id: 1 attributes: name: 'Acme Fashion' slug: acme-fashion properties: status: type: integer example: 200 message: type: string example: 'Store deleted successfully' data: type: object properties: type: type: string example: store id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Acme Fashion' slug: type: string example: acme-fashion 403: description: '' content: application/json: schema: type: object example: errors: - status: '403' code: store.forbidden detail: 'You are not authorized to delete this store' properties: errors: type: array example: - status: '403' code: store.forbidden detail: 'You are not authorized to delete this store' items: type: object properties: status: type: string example: '403' code: type: string example: store.forbidden detail: type: string example: 'You are not authorized to delete this store' 422: description: '' content: application/json: schema: type: object example: errors: - status: '422' code: store.has_related_records detail: 'Cannot delete store because it has related products, categories, options, or orders' properties: errors: type: array example: - status: '422' code: store.has_related_records detail: 'Cannot delete store because it has related products, categories, options, or orders' items: type: object properties: status: type: string example: '422' code: type: string example: store.has_related_records detail: type: string example: 'Cannot delete store because it has related products, categories, options, or orders' tags: - Stores get: summary: 'Get store' operationId: getStore description: 'Return a single store by ID. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: type: store id: 50 attributes: name: 'Morgan Hirthe' slug: morgan-hirthe industry: fashion logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/0077ff?text=qui' color: '#007bff' createdAt: '2026-09-21T17:28:48.000000Z' updatedAt: '2026-09-21T17:28:48.000000Z' relationships: user: data: id: 54 type: user links: self: 'http://dokio.shop/api/v1/users/50' links: self: 'http://dokio.shop/api/v1/users/50' properties: data: type: object properties: type: type: string example: store id: type: integer example: 50 attributes: type: object properties: name: type: string example: 'Morgan Hirthe' slug: type: string example: morgan-hirthe industry: type: string example: fashion logo: type: string example: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/0077ff?text=qui' color: type: string example: '#007bff' createdAt: type: string example: '2026-09-21T17:28:48.000000Z' updatedAt: type: string example: '2026-09-21T17:28:48.000000Z' relationships: type: object properties: user: type: object properties: data: type: object properties: id: type: integer example: 54 type: type: string example: user links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/50' links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/50' tags: - Stores security: [] parameters: - in: path name: slug description: 'The slug of the store.' example: backs required: true schema: type: string - in: path name: store description: 'The store ID.' example: 1 required: true schema: type: integer '/api/v1/users/{id}/store': get: summary: "Get user's store" operationId: getUsersStore description: 'Return the store associated with a user. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - type: store id: 51 attributes: name: 'Rowan Gulgowski' slug: rowan-gulgowski industry: beauty logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/00bb11?text=sunt' color: '#007bff' createdAt: '2026-09-21T17:28:49.000000Z' updatedAt: '2026-09-21T17:28:49.000000Z' relationships: user: data: id: 55 type: user links: self: 'http://dokio.shop/api/v1/users/51' links: self: 'http://dokio.shop/api/v1/users/51' - type: store id: 52 attributes: name: 'Mr. Gilbert Swift V' slug: mr-gilbert-swift-v industry: fashion logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/005500?text=quidem' color: '#007bff' createdAt: '2026-09-21T17:28:49.000000Z' updatedAt: '2026-09-21T17:28:49.000000Z' relationships: user: data: id: 56 type: user links: self: 'http://dokio.shop/api/v1/users/52' links: self: 'http://dokio.shop/api/v1/users/52' properties: data: type: array example: - type: store id: 51 attributes: name: 'Rowan Gulgowski' slug: rowan-gulgowski industry: beauty logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/00bb11?text=sunt' color: '#007bff' createdAt: '2026-09-21T17:28:49.000000Z' updatedAt: '2026-09-21T17:28:49.000000Z' relationships: user: data: id: 55 type: user links: self: 'http://dokio.shop/api/v1/users/51' links: self: 'http://dokio.shop/api/v1/users/51' - type: store id: 52 attributes: name: 'Mr. Gilbert Swift V' slug: mr-gilbert-swift-v industry: fashion logo: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/005500?text=quidem' color: '#007bff' createdAt: '2026-09-21T17:28:49.000000Z' updatedAt: '2026-09-21T17:28:49.000000Z' relationships: user: data: id: 56 type: user links: self: 'http://dokio.shop/api/v1/users/52' links: self: 'http://dokio.shop/api/v1/users/52' items: type: object properties: type: type: string example: store id: type: integer example: 51 attributes: type: object properties: name: type: string example: 'Rowan Gulgowski' slug: type: string example: rowan-gulgowski industry: type: string example: beauty logo: type: string example: 'http://dokio.shop/storage/https://via.placeholder.com/640x480.png/00bb11?text=sunt' color: type: string example: '#007bff' createdAt: type: string example: '2026-09-21T17:28:49.000000Z' updatedAt: type: string example: '2026-09-21T17:28:49.000000Z' relationships: type: object properties: user: type: object properties: data: type: object properties: id: type: integer example: 55 type: type: string example: user links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/51' links: type: object properties: self: type: string example: 'http://dokio.shop/api/v1/users/51' tags: - Stores security: [] parameters: - in: path name: id description: 'The ID of the user.' example: 1 required: true schema: type: integer - in: path name: user description: 'The user ID.' example: 1 required: true schema: type: integer '/api/v1/stores/{store_slug}/products': get: summary: 'List store products' operationId: listStoreProducts description: 'Return all products belonging to a store. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Products retrieved successfully' data: - type: product id: 1 attributes: name: 'Classic Tee' price: '29.99' image: 'https://api.dokio.local/storage/product/tee.jpg' status: 'in stock' relationships: product_variants: data: id: 1 type: product_variant links: self: 'https://api.example.com/products/1' properties: status: type: integer example: 200 message: type: string example: 'Products retrieved successfully' data: type: array example: - type: product id: 1 attributes: name: 'Classic Tee' price: '29.99' image: 'https://api.dokio.local/storage/product/tee.jpg' status: 'in stock' relationships: product_variants: data: id: 1 type: product_variant links: self: 'https://api.example.com/products/1' items: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' price: type: string example: '29.99' image: type: string example: 'https://api.dokio.local/storage/product/tee.jpg' status: type: string example: 'in stock' relationships: type: object properties: product_variants: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: product_variant links: type: object properties: self: type: string example: 'https://api.example.com/products/1' tags: - Products security: [] parameters: - in: path name: store_slug description: 'The slug of the store.' example: backs required: true schema: type: string - in: path name: store description: 'The store ID.' example: 1 required: true schema: type: integer '/api/v1/stores/{store_slug}/products/{slug}': get: summary: 'Get a store product' operationId: getAStoreProduct description: 'Return a single product belonging to a store. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Product retrieved successfully' data: type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee description_html: '
Soft cotton t-shirt in classic fit.
' relationships: product_variants: data: id: 1 type: product_variant includes: product_variants: data: id: 1 type: product_variant attributes: price: '29.99' stock: 100 sku: TSHIRT-BLUE-M image: 'https://api.dokio.local/storage/product/tee.jpg' status: 'in stock' links: self: 'https://api.example.com/products/1' properties: status: type: integer example: 200 message: type: string example: 'Product retrieved successfully' data: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee description_html: type: string example: 'Soft cotton t-shirt in classic fit.
' relationships: type: object properties: product_variants: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: product_variant includes: type: object properties: product_variants: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: product_variant attributes: type: object properties: price: { type: string, example: '29.99' } stock: { type: integer, example: 100 } sku: { type: string, example: TSHIRT-BLUE-M } image: { type: string, example: 'https://api.dokio.local/storage/product/tee.jpg' } status: { type: string, example: 'in stock' } links: type: object properties: self: type: string example: 'https://api.example.com/products/1' tags: - Products security: [] parameters: - in: path name: store_slug description: 'The slug of the store.' example: backs required: true schema: type: string - in: path name: slug description: 'The slug of the product.' example: sds required: true schema: type: string - in: path name: store description: 'The store slug.' example: acme-fashion required: true schema: type: string - in: path name: product description: 'The product slug.' example: classic-tee required: true schema: type: string /api/v1/products: get: summary: 'List products' operationId: listProducts description: 'Return a paginated list of products. Supports filtering by status, search, dates, and includes.' parameters: - in: query name: status description: 'Filter by status. Comma-separated for multiple values.' example: active required: false schema: type: string description: 'Filter by status. Comma-separated for multiple values.' example: active - in: query name: search description: 'Search product name. Use `*` as a wildcard (e.g. `*shirt*`).' example: '*shirt*' required: false schema: type: string description: 'Search product name. Use `*` as a wildcard (e.g. `*shirt*`).' example: '*shirt*' - in: query name: createdAt description: 'Filter by created date (`Y-m-d`) or range (`start,end`).' example: '2024-01-01' required: false schema: type: string description: 'Filter by created date (`Y-m-d`) or range (`start,end`).' example: '2024-01-01' - in: query name: updatedAt description: 'Filter by updated date (`Y-m-d`) or range (`start,end`).' example: '2024-01-01,2024-12-31' required: false schema: type: string description: 'Filter by updated date (`Y-m-d`) or range (`start,end`).' example: '2024-01-01,2024-12-31' - in: query name: include description: 'Eager-load relations (e.g. `product_variants`).' example: product_variants required: false schema: type: string description: 'Eager-load relations (e.g. `product_variants`).' example: product_variants - in: query name: page description: 'Page number for pagination.' example: 1 required: false schema: type: integer description: 'Page number for pagination.' example: 1 responses: 200: description: 'Paginated products' content: application/json: schema: type: object example: status: 200 message: 'Products retrieved successfully' data: - type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee createdAt: '2024-01-15T10:00:00.000000Z' updatedAt: '2024-01-15T10:00:00.000000Z' status: 'in stock' relationships: user: data: id: 1 type: user links: self: 'https://api.dokio.local/api/v1/products/1' properties: status: type: integer example: 200 message: type: string example: 'Products retrieved successfully' data: type: array example: - type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee createdAt: '2024-01-15T10:00:00.000000Z' updatedAt: '2024-01-15T10:00:00.000000Z' status: 'in stock' relationships: user: data: id: 1 type: user links: self: 'https://api.dokio.local/api/v1/products/1' items: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee createdAt: type: string example: '2024-01-15T10:00:00.000000Z' updatedAt: type: string example: '2024-01-15T10:00:00.000000Z' status: type: string example: 'in stock' relationships: type: object properties: user: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: user links: type: object properties: self: type: string example: 'https://api.dokio.local/api/v1/products/1' tags: - Products post: summary: 'Create product' operationId: createProduct description: "Create a product for the authenticated user's store. Send required `description_html` (max 100,000 UTF-8 bytes). HTML is sanitized. Mutation responses omit it; fetch a detail route to read it." parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'Product created successfully' data: type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee createdAt: '2024-01-15T10:00:00.000000Z' updatedAt: '2024-01-15T10:00:00.000000Z' status: null properties: status: type: integer example: 201 message: type: string example: 'Product created successfully' data: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee createdAt: type: string example: '2024-01-15T10:00:00.000000Z' updatedAt: type: string example: '2024-01-15T10:00:00.000000Z' status: type: string example: null nullable: true 422: description: '' content: application/json: schema: type: object example: errors: - status: '422' code: validation.max_utf8_bytes detail: 'This field exceeds the maximum allowed value.' source: pointer: /description_html meta: field: description_html properties: errors: type: array example: - status: '422' code: validation.max_utf8_bytes detail: 'This field exceeds the maximum allowed value.' source: pointer: /description_html meta: field: description_html items: type: object properties: status: type: string example: '422' code: type: string example: validation.max_utf8_bytes detail: type: string example: 'This field exceeds the maximum allowed value.' source: type: object properties: pointer: type: string example: /description_html meta: type: object properties: field: type: string example: description_html tags: - Products requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The product name. Must not be greater than 255 characters.' example: 'Classic Tee' description_html: type: string description: 'Required canonical HTML description. Sanitized against the product allowlist. Maximum 100,000 UTF-8 bytes.' example: 'قطن ناعم — Soft cotton.
' category_id: type: string description: "ID of a category owned by the authenticated user's store." example: 1 required: - name - description_html - category_id '/api/v1/products/{slug}': get: summary: 'Get product' operationId: getProduct description: 'Return a single product. Pass `include=author` to embed the owning user.' parameters: - in: query name: include description: 'Optional related resources to include. Use `author` for the product owner.' example: author required: false schema: type: string description: 'Optional related resources to include. Use `author` for the product owner.' example: author responses: 200: description: '' content: application/json: schema: type: object example: type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee description_html: 'Soft cotton t-shirt in classic fit.
' createdAt: '2024-01-15T10:00:00.000000Z' updatedAt: '2024-01-15T10:00:00.000000Z' relationships: user: data: id: 1 type: user category: data: id: 1 type: category variants: data: - id: 1 type: variant - id: 2 type: variant links: self: 'https://api.dokio.local/api/v1/products/1' properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee description_html: type: string example: 'Soft cotton t-shirt in classic fit.
' createdAt: type: string example: '2024-01-15T10:00:00.000000Z' updatedAt: type: string example: '2024-01-15T10:00:00.000000Z' relationships: type: object properties: user: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: user category: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: category variants: type: object properties: data: type: array example: - id: 1 type: variant - id: 2 type: variant items: type: object properties: id: type: integer example: 1 type: type: string example: variant links: type: object properties: self: type: string example: 'https://api.dokio.local/api/v1/products/1' tags: - Products put: summary: 'Update product' operationId: updateProduct description: 'Replace name, HTML description, and category of an owned product identified by slug. Send required `description_html` (max 100,000 UTF-8 bytes). HTML is sanitized. Mutation responses omit it; fetch a detail route to read it. Dashboard clients may POST multipart with `_method=PUT`.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Product updated successfully' data: type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee createdAt: '2024-01-15T10:00:00.000000Z' updatedAt: '2024-01-15T10:00:00.000000Z' status: null properties: status: type: integer example: 200 message: type: string example: 'Product updated successfully' data: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee createdAt: type: string example: '2024-01-15T10:00:00.000000Z' updatedAt: type: string example: '2024-01-15T10:00:00.000000Z' status: type: string example: null nullable: true 422: description: '' content: application/json: schema: type: object example: errors: - status: '422' code: validation.max_utf8_bytes detail: 'This field exceeds the maximum allowed value.' source: pointer: /description_html meta: field: description_html properties: errors: type: array example: - status: '422' code: validation.max_utf8_bytes detail: 'This field exceeds the maximum allowed value.' source: pointer: /description_html meta: field: description_html items: type: object properties: status: type: string example: '422' code: type: string example: validation.max_utf8_bytes detail: type: string example: 'This field exceeds the maximum allowed value.' source: type: object properties: pointer: type: string example: /description_html meta: type: object properties: field: type: string example: description_html tags: - Products requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The product name. Must not be greater than 255 characters.' example: 'Classic Tee' description_html: type: string description: 'Required canonical HTML description. Sanitized against the product allowlist. Maximum 100,000 UTF-8 bytes.' example: 'قطن ناعم — Soft cotton.
' category_id: type: string description: "ID of a category owned by the authenticated user's store." example: 1 required: - name - description_html - category_id delete: summary: 'Delete product' operationId: deleteProduct description: 'Remove a product when it has no related variants.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Product deleted successfully' data: type: product id: 1 attributes: name: 'Classic Tee' slug: classic-tee properties: status: type: integer example: 200 message: type: string example: 'Product deleted successfully' data: type: object properties: type: type: string example: product id: type: integer example: 1 attributes: type: object properties: name: type: string example: 'Classic Tee' slug: type: string example: classic-tee 422: description: '' content: application/json: schema: type: object example: errors: - status: '422' code: product.has_variants detail: 'Cannot delete product because it has related variants' properties: errors: type: array example: - status: '422' code: product.has_variants detail: 'Cannot delete product because it has related variants' items: type: object properties: status: type: string example: '422' code: type: string example: product.has_variants detail: type: string example: 'Cannot delete product because it has related variants' tags: - Products parameters: - in: path name: slug description: 'The slug of the product.' example: sds required: true schema: type: string - in: path name: product description: 'The product slug.' example: classic-tee required: true schema: type: string '/api/v1/stores/{store_slug}/categories': get: summary: 'List store categories' operationId: listStoreCategories description: 'Return all categories belonging to a store. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Categories retrieved successfully' data: - type: category id: 1 attributes: name: Apparel slug: apparel image: 'https://api.dokio.local/storage/category/apparel.jpg' status: active relationships: store: data: id: 1 type: store links: self: 'https://api.dokio.local/api/v1/stores/1' properties: status: type: integer example: 200 message: type: string example: 'Categories retrieved successfully' data: type: array example: - type: category id: 1 attributes: name: Apparel slug: apparel image: 'https://api.dokio.local/storage/category/apparel.jpg' status: active items: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel image: type: string example: 'https://api.dokio.local/storage/category/apparel.jpg' status: type: string example: active relationships: type: object properties: store: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: store links: type: object properties: self: type: string example: 'https://api.dokio.local/api/v1/stores/1' tags: - Categories security: [] parameters: - in: path name: store_slug description: 'The slug of the store.' example: backs required: true schema: type: string - in: path name: store description: 'The store slug.' example: acme-fashion required: true schema: type: string '/api/v1/stores/{store_slug}/categories/{slug}': get: summary: 'Get a store category' operationId: getAStoreCategory description: 'Return a single category belonging to a store. This endpoint is public.' parameters: - in: query name: include description: 'Optional related resources to include. Use `products` for the category products to be included.' example: products required: false schema: type: string description: 'Optional related resources to include. Use `products` for the category products to be included.' example: products responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Category retrieved successfully' data: type: category id: 1 attributes: name: Apparel slug: apparel image: 'https://api.dokio.local/storage/category/apparel.jpg' status: active relationships: store: data: id: 1 type: store links: self: 'https://api.dokio.local/api/v1/stores/1/categories/1' properties: status: type: integer example: 200 message: type: string example: 'Category retrieved successfully' data: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel image: type: string example: 'https://api.dokio.local/storage/category/apparel.jpg' status: type: string example: active relationships: type: object properties: store: type: object properties: data: type: object properties: id: type: integer example: 1 type: type: string example: store links: type: object properties: self: type: string example: 'https://api.dokio.local/api/v1/stores/1/categories/1' tags: - Categories security: [] parameters: - in: path name: store_slug description: 'The slug of the store.' example: backs required: true schema: type: string - in: path name: slug description: 'The slug of the category.' example: backpacks required: true schema: type: string - in: path name: 'The store slug.' description: '' example: acme-fashion required: true schema: type: string - in: path name: 'The category slug.' description: '' example: apparel required: true schema: type: string /api/v1/categories: get: summary: 'List categories' operationId: listCategories description: 'Return all categories.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Categories retrieved successfully' data: - type: category id: 1 attributes: name: Apparel slug: apparel image: 'https://api.dokio.local/storage/category/apparel.jpg' status: active properties: status: type: integer example: 200 message: type: string example: 'Categories retrieved successfully' data: type: array example: - type: category id: 1 attributes: name: Apparel slug: apparel image: 'https://api.dokio.local/storage/category/apparel.jpg' status: active items: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel image: type: string example: 'https://api.dokio.local/storage/category/apparel.jpg' status: type: string example: active tags: - Categories post: summary: 'Create category' operationId: createCategory description: 'Create a new category. Accepts `multipart/form-data` when uploading an image.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'Category created successfully' data: type: category id: 1 attributes: name: Apparel slug: apparel status: active properties: status: type: integer example: 201 message: type: string example: 'Category created successfully' data: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel status: type: string example: active 403: description: '' content: application/json: schema: type: object example: errors: - status: '403' code: store.required detail: 'User must have a store to create a category.' properties: errors: type: array example: - status: '403' code: store.required detail: 'User must have a store to create a category.' items: type: object properties: status: type: string example: '403' code: type: string example: store.required detail: type: string example: 'User must have a store to create a category.' tags: - Categories requestBody: required: true content: multipart/form-data: schema: type: object properties: name: type: string description: 'The category name. Must not be greater than 255 characters.' example: Apparel image: type: string format: binary description: 'Category image file (jpeg, png, jpg, gif, or svg; max 2MB). SVG uploads are sanitized before storage. Must be an image. Must not be greater than 2048 kilobytes.' required: - name - image '/api/v1/categories/{slug}': get: summary: 'Get category' operationId: getCategory description: 'Return a single category by ID.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Category retrieved successfully' data: type: category id: 1 attributes: name: Apparel slug: apparel status: active properties: status: type: integer example: 200 message: type: string example: 'Category retrieved successfully' data: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel status: type: string example: active tags: - Categories put: summary: 'Update category' operationId: updateCategory description: 'Update an existing category. Accepts `multipart/form-data` when uploading a new image.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Category updated successfully' data: type: category id: 1 attributes: name: Apparel slug: apparel status: active properties: status: type: integer example: 200 message: type: string example: 'Category updated successfully' data: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel status: type: string example: active tags: - Categories requestBody: required: true content: multipart/form-data: schema: type: object properties: name: type: string description: 'The category name. Must not be greater than 255 characters.' example: Apparel image: type: string format: binary description: 'Optional new category image (jpeg, png, jpg, gif, or svg; max 2MB). SVG uploads are sanitized before storage. Must be an image. Must not be greater than 2048 kilobytes.' status: type: string description: 'Category status.' example: active enum: - active - inactive required: - name - status delete: summary: 'Delete category' operationId: deleteCategory description: 'Remove a category when it has no related products.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: 200 message: 'Category deleted successfully' data: type: category id: 1 attributes: name: Apparel slug: apparel status: active properties: status: type: integer example: 200 message: type: string example: 'Category deleted successfully' data: type: object properties: type: type: string example: category id: type: integer example: 1 attributes: type: object properties: name: type: string example: Apparel slug: type: string example: apparel status: type: string example: active 422: description: '' content: application/json: schema: type: object example: errors: - status: '422' code: category.has_products detail: 'Cannot delete category because it has related products' properties: errors: type: array example: - status: '422' code: category.has_products detail: 'Cannot delete category because it has related products' items: type: object properties: status: type: string example: '422' code: type: string example: category.has_products detail: type: string example: 'Cannot delete category because it has related products' tags: - Categories parameters: - in: path name: slug description: 'The slug of the category.' example: backpacks required: true schema: type: string - in: path name: category description: 'The category ID.' example: 1 required: true schema: type: integer /api/v1/enums/industries: get: summary: 'List industries' operationId: listIndustries description: 'Return the allowed industry values for store creation and updates. This endpoint is public.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - fashion - electronics - food - beauty - home - other properties: data: type: array example: - fashion - electronics - food - beauty - home - other items: type: string tags: - Enums security: [] '/api/v1/stores/{store_slug}/checkout': post: summary: 'Place COD order' operationId: placeCODOrder description: 'Create a cash-on-delivery order from line items. No cart is stored on the server.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: status: 201 message: 'Order placed successfully' data: type: order id: 1 attributes: number: 01HXYZ guest_name: 'Jane Doe' guest_email: jane@example.com guest_phone: '01001234567' status: pending subtotal: 200 shipping_amount: 0 total: 200 properties: status: type: integer example: 201 message: type: string example: 'Order placed successfully' data: type: object properties: type: type: string example: order id: type: integer example: 1 attributes: type: object properties: number: type: string example: 01HXYZ guest_name: type: string example: 'Jane Doe' guest_email: type: string example: jane@example.com guest_phone: type: string example: '01001234567' status: type: string example: pending subtotal: type: integer example: 200 shipping_amount: type: integer example: 0 total: type: integer example: 200 422: description: 'Validation failed (empty items, inactive variant, insufficient stock, or foreign-store variant).' content: application/json: schema: type: object example: errors: - status: '422' code: validation.insufficient_stock detail: 'The submitted value is invalid.' meta: field: items properties: errors: type: array example: - status: '422' code: validation.insufficient_stock detail: 'The submitted value is invalid.' meta: field: items items: type: object properties: status: type: string example: '422' code: type: string example: validation.insufficient_stock detail: type: string example: 'The submitted value is invalid.' meta: type: object properties: field: type: string example: items tags: - Checkout requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Guest full name. Must not be greater than 255 characters.' example: 'Jane Doe' email: type: string description: 'Guest email used for order tracking. Must be a valid email address. Must not be greater than 255 characters.' example: jane@example.com phone: type: string description: 'Guest phone number for COD delivery. Must not be greater than 50 characters.' example: '01001234567' line1: type: string description: 'Shipping street address. Must not be greater than 255 characters.' example: '12 Nile Street' city: type: string description: 'Shipping city. Must not be greater than 100 characters.' example: Cairo governorate: type: string description: 'Shipping governorate / state. Must not be greater than 100 characters.' example: Cairo postal_code: type: string description: 'Optional postal code. Must not be greater than 20 characters.' example: '11511' nullable: true country: type: string description: 'ISO 3166-1 alpha-2 country code. Defaults to EG. Must not be greater than 255 characters.' example: EG nullable: true items: type: array description: 'Line items to purchase. Duplicate variant ids are merged by summing quantities. Must have at least 1 items.' example: - product_variant_id: 1 quantity: 2 items: type: object properties: product_variant_id: type: integer description: 'Sellable product variant id. Theid of an existing record in the product_variants table.'
example: 1
quantity:
type: integer
description: 'Quantity to order for this variant. Must be at least 1.'
example: 2
required:
- product_variant_id
- quantity
required:
- name
- email
- phone
- line1
- city
- governorate
- items
security: []
parameters:
-
in: path
name: store_slug
description: 'The slug of the store.'
example: backs
required: true
schema:
type: string
-
in: path
name: store
description: 'Store slug or id.'
example: my-store
required: true
schema:
type: string
'/api/v1/product-options/{productOption_id}/product-option-values':
get:
summary: 'List product option values'
operationId: listProductOptionValues
description: 'Return all product option values.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product option values retrieved successfully'
data:
-
type: product_option_value
id: 1
attributes:
value: Small
product_option_id: 1
relationships:
product_option:
data:
id: 1
type: product_option
links:
self: 'https://api.example.com/product-option-values/1'
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product option values retrieved successfully'
data:
type: array
example:
-
type: product_option_value
id: 1
attributes:
value: Small
product_option_id: 1
relationships:
product_option:
data:
id: 1
type: product_option
links:
self: 'https://api.example.com/product-option-values/1'
items:
type: object
properties:
type:
type: string
example: product_option_value
id:
type: integer
example: 1
attributes:
type: object
properties:
value:
type: string
example: Small
product_option_id:
type: integer
example: 1
relationships:
type: object
properties:
product_option:
type: object
properties:
data:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: product_option
links:
type: object
properties:
self:
type: string
example: 'https://api.example.com/product-option-values/1'
tags:
- Endpoints
parameters:
-
in: path
name: productOption_id
description: 'The ID of the productOption.'
example: 1
required: true
schema:
type: integer
/api/v1/product-option-values:
post:
summary: 'Create product option values'
operationId: createProductOptionValues
description: 'Create multiple product option values for the same option.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
status: 201
message: 'Product option values created successfully'
data:
-
type: product_option_value
id: 1
attributes:
value: Small
product_option_id: 1
relationships:
product_option:
type: product_option
id: 1
links:
self: 'https://api.dokio.local/api/v1/product-option-values/1'
properties:
status:
type: integer
example: 201
message:
type: string
example: 'Product option values created successfully'
data:
type: array
example:
-
type: product_option_value
id: 1
attributes:
value: Small
product_option_id: 1
relationships:
product_option:
type: product_option
id: 1
links:
self: 'https://api.dokio.local/api/v1/product-option-values/1'
items:
type: object
properties:
type:
type: string
example: product_option_value
id:
type: integer
example: 1
attributes:
type: object
properties:
value:
type: string
example: Small
product_option_id:
type: integer
example: 1
relationships:
type: object
properties:
product_option:
type: object
properties:
type:
type: string
example: product_option
id:
type: integer
example: 1
links:
type: object
properties:
self:
type: string
example: 'https://api.dokio.local/api/v1/product-option-values/1'
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
product_option_id:
type: string
description: 'The id of an existing record in the product_options table.'
example: architecto
values:
type: array
description: 'Must not be greater than 255 characters.'
example:
- 'n'
items:
type: string
required:
- product_option_id
- values
'/api/v1/product-option-values/{id}':
get:
summary: 'Display the specified resource.'
operationId: displayTheSpecifiedResource
description: ''
parameters: []
responses: { }
tags:
- Endpoints
put:
summary: 'Update the specified resource in storage.'
operationId: updateTheSpecifiedResourceInStorage
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: 'Delete product option value'
operationId: deleteProductOptionValue
description: 'Remove a product option value when it has no related variants.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product option value deleted successfully'
data:
type: product_option_value
id: 1
attributes:
value: Small
product_option_id: 1
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product option value deleted successfully'
data:
type: object
properties:
type:
type: string
example: product_option_value
id:
type: integer
example: 1
attributes:
type: object
properties:
value:
type: string
example: Small
product_option_id:
type: integer
example: 1
403:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '403'
code: product_option_value.forbidden
detail: 'You are not authorized to delete this product option value'
properties:
errors:
type: array
example:
-
status: '403'
code: product_option_value.forbidden
detail: 'You are not authorized to delete this product option value'
items:
type: object
properties:
status:
type: string
example: '403'
code:
type: string
example: product_option_value.forbidden
detail:
type: string
example: 'You are not authorized to delete this product option value'
422:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '422'
code: product_option_value.has_variants
detail: 'Cannot delete product option value because it has related variants'
properties:
errors:
type: array
example:
-
status: '422'
code: product_option_value.has_variants
detail: 'Cannot delete product option value because it has related variants'
items:
type: object
properties:
status:
type: string
example: '422'
code:
type: string
example: product_option_value.has_variants
detail:
type: string
example: 'Cannot delete product option value because it has related variants'
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the product option value.'
example: 1
required: true
schema:
type: integer
/api/v1/orders:
get:
summary: 'List orders'
operationId: listOrders
description: 'Return orders for the authenticated vendor store.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Orders retrieved successfully'
data:
-
type: order
id: 1
attributes:
number: 01HXYZ
status: pending
total: 200
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Orders retrieved successfully'
data:
type: array
example:
-
type: order
id: 1
attributes:
number: 01HXYZ
status: pending
total: 200
items:
type: object
properties:
type:
type: string
example: order
id:
type: integer
example: 1
attributes:
type: object
properties:
number:
type: string
example: 01HXYZ
status:
type: string
example: pending
total:
type: integer
example: 200
tags:
- Orders
'/api/v1/orders/{id}':
get:
summary: 'Show order'
operationId: showOrder
description: 'Return a single store order.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Order retrieved successfully'
data:
type: order
id: 1
attributes:
number: 01HXYZ
status: pending
total: 200
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Order retrieved successfully'
data:
type: object
properties:
type:
type: string
example: order
id:
type: integer
example: 1
attributes:
type: object
properties:
number:
type: string
example: 01HXYZ
status:
type: string
example: pending
total:
type: integer
example: 200
tags:
- Orders
patch:
summary: 'Update order'
operationId: updateOrder
description: 'Update order, payment, and/or shipment status.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Order updated successfully'
data:
type: order
id: 1
attributes:
number: 01HXYZ
status: confirmed
total: 200
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Order updated successfully'
data:
type: object
properties:
type:
type: string
example: order
id:
type: integer
example: 1
attributes:
type: object
properties:
number:
type: string
example: 01HXYZ
status:
type: string
example: confirmed
total:
type: integer
example: 200
tags:
- Orders
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: 'Order status. Allowed: pending, confirmed, cancelled, completed.'
example: confirmed
enum:
- pending
- confirmed
- cancelled
- completed
payment_status:
type: string
description: 'Payment status. Allowed: unpaid, paid.'
example: paid
enum:
- unpaid
- paid
shipment_status:
type: string
description: 'Shipment status. Allowed: pending, shipped, delivered, cancelled.'
example: shipped
enum:
- pending
- shipped
- delivered
- cancelled
parameters:
-
in: path
name: id
description: 'The ID of the order.'
example: 1
required: true
schema:
type: integer
-
in: path
name: order
description: 'Order id.'
example: 1
required: true
schema:
type: integer
'/api/v1/orders/{number}':
get:
summary: 'Track guest order'
operationId: trackGuestOrder
description: 'Look up an order by public number and guest email.'
parameters:
-
in: query
name: email
description: 'Guest email used when placing the order.'
example: jane@example.com
required: true
schema:
type: string
description: 'Guest email used when placing the order.'
example: jane@example.com
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Order retrieved successfully'
data:
type: order
id: 1
attributes:
number: 01HXYZ
status: pending
total: 200
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Order retrieved successfully'
data:
type: object
properties:
type:
type: string
example: order
id:
type: integer
example: 1
attributes:
type: object
properties:
number:
type: string
example: 01HXYZ
status:
type: string
example: pending
total:
type: integer
example: 200
404:
description: ''
content:
application/json:
schema:
type: object
example:
status: 404
message: 'Order not found'
properties:
status:
type: integer
example: 404
message:
type: string
example: 'Order not found'
tags:
- Orders
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Must be a valid email address.'
example: gbailey@example.net
required:
- email
security: []
parameters:
-
in: path
name: number
description: 'Public order number returned at checkout.'
example: 01HXYZ
required: true
schema:
type: string
/api/v1/product-options:
get:
summary: 'List product options'
operationId: listProductOptions
description: 'Return all product options.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product options retrieved successfully'
data:
-
type: product_option
id: 1
attributes:
name: Apparel
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product options retrieved successfully'
data:
type: array
example:
-
type: product_option
id: 1
attributes:
name: Apparel
items:
type: object
properties:
type:
type: string
example: product_option
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Apparel
tags:
- 'Product Options'
post:
summary: 'Create product option'
operationId: createProductOption
description: 'Create a new product option.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
status: 201
message: 'Product option created successfully'
data:
type: product_option
id: 1
attributes:
name: Size
properties:
status:
type: integer
example: 201
message:
type: string
example: 'Product option created successfully'
data:
type: object
properties:
type:
type: string
example: product_option
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Size
tags:
- 'Product Options'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
required:
- name
'/api/v1/product-options/{id}':
get:
summary: 'Display the specified resource.'
operationId: displayTheSpecifiedResource
description: ''
parameters: []
responses: { }
tags:
- 'Product Options'
put:
summary: 'Update product option'
operationId: updateProductOption
description: 'Rename an owned product option. JSON PUT/PATCH and multipart POST with `_method=PUT` are supported. Identity, store, and related values stay unchanged.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product option updated successfully'
data:
type: product_option
id: 1
attributes:
name: Size
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product option updated successfully'
data:
type: object
properties:
type:
type: string
example: product_option
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Size
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
422:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '422'
code: validation.required
detail: 'This field is required.'
meta:
field: request
properties:
errors:
type: array
example:
-
status: '422'
code: validation.required
detail: 'This field is required.'
meta:
field: request
items:
type: object
properties:
status:
type: string
example: '422'
code:
type: string
example: validation.required
detail:
type: string
example: 'This field is required.'
meta:
type: object
properties:
field:
type: string
example: request
tags:
- 'Product Options'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Non-blank option name, max 255 characters. Must not be greater than 255 characters.'
example: Size
store_id:
type: string
description: ''
example: null
delete:
summary: 'Delete product option'
operationId: deleteProductOption
description: 'Remove a product option when it has no related values.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product option deleted successfully'
data:
type: product_option
id: 1
attributes:
name: Size
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product option deleted successfully'
data:
type: object
properties:
type:
type: string
example: product_option
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Size
422:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '422'
code: product_option.has_values
detail: 'Cannot delete product option because it has related values'
properties:
errors:
type: array
example:
-
status: '422'
code: product_option.has_values
detail: 'Cannot delete product option because it has related values'
items:
type: object
properties:
status:
type: string
example: '422'
code:
type: string
example: product_option.has_values
detail:
type: string
example: 'Cannot delete product option because it has related values'
tags:
- 'Product Options'
parameters:
-
in: path
name: id
description: 'The ID of the product option.'
example: 1
required: true
schema:
type: integer
/api/v1/product-variants:
get:
summary: 'List product variants'
operationId: listProductVariants
description: 'Return all product variants.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product variants retrieved successfully'
data:
-
type: product_variant
id: 1
attributes:
name: Small
price: 100
stock: 100
product_id: 1
product_option_id: 1
relationships:
product:
data:
id: 1
type: product
product_option_value:
data:
id: 1
type: product_option_value
links:
self: 'https://api.example.com/product-variants/1'
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product variants retrieved successfully'
data:
type: array
example:
-
type: product_variant
id: 1
attributes:
name: Small
price: 100
stock: 100
product_id: 1
product_option_id: 1
relationships:
product:
data:
id: 1
type: product
product_option_value:
data:
id: 1
type: product_option_value
links:
self: 'https://api.example.com/product-variants/1'
items:
type: object
properties:
type:
type: string
example: product_variant
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Small
price:
type: integer
example: 100
stock:
type: integer
example: 100
product_id:
type: integer
example: 1
product_option_id:
type: integer
example: 1
relationships:
type: object
properties:
product:
type: object
properties:
data:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: product
product_option_value:
type: object
properties:
data:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: product_option_value
links:
type: object
properties:
self:
type: string
example: 'https://api.example.com/product-variants/1'
tags:
- 'Product Variants'
post:
summary: 'Create product variant'
operationId: createProductVariant
description: 'Create a new product variant.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
status: 201
message: 'Product variant created successfully'
data:
type: product_variant
id: 1
attributes:
name: Small
price: 100
stock: 100
product_id: 1
product_option_id: 1
properties:
status:
type: integer
example: 201
message:
type: string
example: 'Product variant created successfully'
data:
type: object
properties:
type:
type: string
example: product_variant
id:
type: integer
example: 1
attributes:
type: object
properties:
name:
type: string
example: Small
price:
type: integer
example: 100
stock:
type: integer
example: 100
product_id:
type: integer
example: 1
product_option_id:
type: integer
example: 1
tags:
- 'Product Variants'
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
price:
type: number
description: 'The price of the product variant. Must be at least 0.'
example: 100.0
stock:
type: integer
description: 'The stock of the product variant. Must be at least 0.'
example: 100
product_id:
type: string
description: 'The product ID of the product variant. The id of an existing record in the products table.'
example: 1
product_option_value_id:
type: string
description: 'The product option value ID of the product variant. The id of an existing record in the product_option_values table.'
example: 1
sku:
type: string
description: 'The SKU of the product variant. Must not be greater than 255 characters.'
example: '1234567890'
image:
type: string
format: binary
description: 'The image of the product variant (multipart file upload). Must be an image. Must not be greater than 2048 kilobytes.'
status:
type: string
description: 'The status of the product variant.'
example: active
enum:
- active
- inactive
required:
- price
- stock
- product_id
- product_option_value_id
- sku
- image
- status
'/api/v1/product-variants/{id}':
get:
summary: 'Display the specified resource.'
operationId: displayTheSpecifiedResource
description: ''
parameters: []
responses: { }
tags:
- 'Product Variants'
put:
summary: 'Update product variant'
operationId: updateProductVariant
description: 'Update supplied fields on an owned product variant. JSON PUT/PATCH and multipart POST with `_method=PUT` are supported. Omitted fields, including the image, remain unchanged.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product variant updated successfully'
data:
type: product_variant
id: 1
attributes:
price: 12500
stock: 8
sku: TEE-M
status: active
product_id: 1
product_option_value_id: 1
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product variant updated successfully'
data:
type: object
properties:
type:
type: string
example: product_variant
id:
type: integer
example: 1
attributes:
type: object
properties:
price:
type: integer
example: 12500
stock:
type: integer
example: 8
sku:
type: string
example: TEE-M
status:
type: string
example: active
product_id:
type: integer
example: 1
product_option_value_id:
type: integer
example: 1
401:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '401'
code: unauthenticated
detail: Unauthenticated.
properties:
errors:
type: array
example:
-
status: '401'
code: unauthenticated
detail: Unauthenticated.
items:
type: object
properties:
status:
type: string
example: '401'
code:
type: string
example: unauthenticated
detail:
type: string
example: Unauthenticated.
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
422:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '422'
code: validation.required
detail: 'This field is required.'
meta:
field: request
properties:
errors:
type: array
example:
-
status: '422'
code: validation.required
detail: 'This field is required.'
meta:
field: request
items:
type: object
properties:
status:
type: string
example: '422'
code:
type: string
example: validation.required
detail:
type: string
example: 'This field is required.'
meta:
type: object
properties:
field:
type: string
example: request
tags:
- 'Product Variants'
requestBody:
required: false
content:
multipart/form-data:
schema:
type: object
properties:
price:
type: integer
description: 'Integer minor-unit price from 0 to 2147483647. Omitted value is unchanged. Must be at least 0. Must not be greater than 2147483647.'
example: 12500
stock:
type: integer
description: 'Absolute integer stock from 0 to 2147483647. Omitted value is unchanged. Must be at least 0. Must not be greater than 2147483647.'
example: 8
sku:
type: string
description: 'Non-blank SKU, max 255 characters. No uniqueness rule is applied. Must not be greater than 255 characters.'
example: TEE-M
status:
type: string
description: 'Availability status. Allowed: active, inactive.'
example: active
enum:
- active
- inactive
image:
type: string
format: binary
description: 'Replacement image upload (jpeg, png, jpg, gif, or svg; max 2MB). SVG uploads are sanitized. Omitted image is retained; URLs and null are rejected. Must be an image. Must not be greater than 2048 kilobytes.'
product_option_value_id:
type: integer
description: 'Replacement option value that belongs to the authenticated store. The id of an existing record in the product_option_values table.'
example: 1
product_id:
type: integer
description: 'Optional compatibility echo. Must equal the current product ID and is not persisted as a change.'
example: 1
store_id:
type: string
description: ''
example: null
delete:
summary: 'Delete product variant'
operationId: deleteProductVariant
description: 'Remove a product variant when it is not used in any orders.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Product variant deleted successfully'
data:
type: product_variant
id: 1
attributes:
stock: 100
price: 100
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Product variant deleted successfully'
data:
type: object
properties:
type:
type: string
example: product_variant
id:
type: integer
example: 1
attributes:
type: object
properties:
stock:
type: integer
example: 100
price:
type: integer
example: 100
403:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '403'
code: product_variant.forbidden
detail: 'You are not authorized to delete this product variant'
properties:
errors:
type: array
example:
-
status: '403'
code: product_variant.forbidden
detail: 'You are not authorized to delete this product variant'
items:
type: object
properties:
status:
type: string
example: '403'
code:
type: string
example: product_variant.forbidden
detail:
type: string
example: 'You are not authorized to delete this product variant'
422:
description: ''
content:
application/json:
schema:
type: object
example:
errors:
-
status: '422'
code: product_variant.used_in_orders
detail: 'Cannot delete product variant because it is used in orders'
properties:
errors:
type: array
example:
-
status: '422'
code: product_variant.used_in_orders
detail: 'Cannot delete product variant because it is used in orders'
items:
type: object
properties:
status:
type: string
example: '422'
code:
type: string
example: product_variant.used_in_orders
detail:
type: string
example: 'Cannot delete product variant because it is used in orders'
tags:
- 'Product Variants'
parameters:
-
in: path
name: id
description: 'The ID of the product variant.'
example: 3
required: true
schema:
type: integer
'/api/v1/stores/{store_slug}/rows':
get:
summary: 'List public store rows'
operationId: listPublicStoreRows
description: 'Return visible homepage sections for a store slug, with slides. Hidden and unknown types are omitted.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Rows retrieved successfully'
data:
-
type: store_row
id: 12
attributes:
type: hero_slider
position: 1
isVisible: true
settings:
autoplay: true
interval: 5000
height: large
overlay: dark
includes:
slides: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Rows retrieved successfully'
data:
type: array
example:
-
type: store_row
id: 12
attributes:
type: hero_slider
position: 1
isVisible: true
settings:
autoplay: true
interval: 5000
height: large
overlay: dark
includes:
slides: []
items:
type: object
properties:
type:
type: string
example: store_row
id:
type: integer
example: 12
attributes:
type: object
properties:
type:
type: string
example: hero_slider
position:
type: integer
example: 1
isVisible:
type: boolean
example: true
settings:
type: object
properties:
autoplay:
type: boolean
example: true
interval:
type: integer
example: 5000
height:
type: string
example: large
overlay:
type: string
example: dark
includes:
type: object
properties:
slides:
type: array
example: []
tags:
- 'Storefront rows'
security: []
parameters:
-
in: path
name: store_slug
description: 'The slug of the store.'
example: backs
required: true
schema:
type: string
-
in: path
name: store
description: 'The store slug.'
example: acme-fashion
required: true
schema:
type: string
/api/v1/store/rows:
get:
summary: 'List store rows'
operationId: listStoreRows
description: 'Return every homepage section for the authenticated merchant store, including hidden rows and slides.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Rows retrieved successfully'
data:
-
type: store_row
id: 12
attributes:
type: hero_slider
position: 1
isVisible: true
settings:
autoplay: true
interval: 5000
height: large
overlay: dark
createdAt: '2026-09-10T00:00:00.000000Z'
updatedAt: '2026-09-10T00:00:00.000000Z'
relationships:
store:
data:
type: store
id: 1
slides:
data: []
includes:
slides: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Rows retrieved successfully'
data:
type: array
example:
-
type: store_row
id: 12
attributes:
type: hero_slider
position: 1
isVisible: true
settings:
autoplay: true
interval: 5000
height: large
overlay: dark
createdAt: '2026-09-10T00:00:00.000000Z'
updatedAt: '2026-09-10T00:00:00.000000Z'
relationships:
store:
data:
type: store
id: 1
slides:
data: []
includes:
slides: []
items:
type: object
properties:
type:
type: string
example: store_row
id:
type: integer
example: 12
attributes:
type: object
properties:
type:
type: string
example: hero_slider
position:
type: integer
example: 1
isVisible:
type: boolean
example: true
settings:
type: object
properties:
autoplay:
type: boolean
example: true
interval:
type: integer
example: 5000
height:
type: string
example: large
overlay:
type: string
example: dark
createdAt:
type: string
example: '2026-09-10T00:00:00.000000Z'
updatedAt:
type: string
example: '2026-09-10T00:00:00.000000Z'
relationships:
type: object
properties:
store:
type: object
properties:
data:
type: object
properties:
type:
type: string
example: store
id:
type: integer
example: 1
slides:
type: object
properties:
data:
type: array
example: []
includes:
type: object
properties:
slides:
type: array
example: []
tags:
- 'Storefront rows'
post:
summary: 'Create store row'
operationId: createStoreRow
description: 'Append a supported homepage section to the authenticated merchant store.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
status: 201
message: 'Row created successfully'
data:
type: store_row
id: 19
attributes:
type: banner
position: 5
isVisible: false
settings:
heading: Summer
alt: ''
link: /products
image: null
properties:
status:
type: integer
example: 201
message:
type: string
example: 'Row created successfully'
data:
type: object
properties:
type:
type: string
example: store_row
id:
type: integer
example: 19
attributes:
type: object
properties:
type:
type: string
example: banner
position:
type: integer
example: 5
isVisible:
type: boolean
example: false
settings:
type: object
properties:
heading:
type: string
example: Summer
alt:
type: string
example: ''
link:
type: string
example: /products
image:
type: string
example: null
nullable: true
tags:
- 'Storefront rows'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
type:
type: string
description: 'Supported homepage section type.'
example: banner
enum:
- hero_slider
- featured_products
- categories
- welcome_marquee
- banner
settings:
type: object
description: 'Optional type-specific settings. Omitted keys use defaults.'
example:
heading: Summer
properties:
autoplay:
type: boolean
description: ''
example: false
interval:
type: integer
description: 'Must be at least 2000. Must not be greater than 15000.'
example: 1
height:
type: string
description: ''
example: medium
enum:
- small
- medium
- large
overlay:
type: string
description: ''
example: dark
enum:
- none
- light
- dark
limit:
type: integer
description: 'Must be at least 4. Must not be greater than 12.'
example: 4
heading:
type: string
description: 'Must not be greater than 80 characters.'
example: Summer
nullable: true
message:
type: string
description: 'Must not be greater than 120 characters.'
example: g
nullable: true
alt:
type: string
description: 'Must not be greater than 120 characters.'
example: z
nullable: true
link:
type: string
description: 'Must not be greater than 2048 characters.'
example: m
nullable: true
productIds:
type: array
description: ''
example:
- 16
items:
type: integer
categoryIds:
type: array
description: ''
example:
- 16
items:
type: integer
image:
type: string
description: ''
example: null
required:
- type
/api/v1/store/rows/reorder:
patch:
summary: 'Reorder store rows'
operationId: reorderStoreRows
description: 'Rewrite every homepage section position for the authenticated store. The body must list every current row exactly once.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Rows reordered successfully'
data: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Rows reordered successfully'
data:
type: array
example: []
tags:
- 'Storefront rows'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
rows:
type: array
description: 'Every current section exactly once. Positions may be any unique integers; the server stores dense 1..n in array order. Must have at least 1 items.'
example:
-
id: 12
position: 1
-
id: 8
position: 2
items:
type: object
properties:
id:
type: integer
description: ''
example: 16
position:
type: integer
description: ''
example: 16
required:
- id
- position
required:
- rows
'/api/v1/store/rows/{id}':
patch:
summary: 'Update store row'
operationId: updateStoreRow
description: 'Update visibility or settings for one homepage section. Type cannot change. Banner images are uploaded as `image`, not as `settings.image`.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Row updated successfully'
data:
type: store_row
id: 12
attributes:
type: featured_products
isVisible: true
settings:
limit: 8
heading: 'Our picks'
productIds:
- 14
- 9
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Row updated successfully'
data:
type: object
properties:
type:
type: string
example: store_row
id:
type: integer
example: 12
attributes:
type: object
properties:
type:
type: string
example: featured_products
isVisible:
type: boolean
example: true
settings:
type: object
properties:
limit:
type: integer
example: 8
heading:
type: string
example: 'Our picks'
productIds:
type: array
example:
- 14
- 9
items:
type: integer
tags:
- 'Storefront rows'
requestBody:
required: false
content:
multipart/form-data:
schema:
type: object
properties:
type:
type: string
description: 'Rejected on update. Section type is immutable.'
example: null
is_visible:
type: boolean
description: 'Whether customers should see this section.'
example: true
image:
type: string
format: binary
description: 'Banner image upload. Ignored for other section types. Must be an image. Must not be greater than 2048 kilobytes.'
settings:
type: object
description: 'Type-specific settings. Unknown keys are rejected. Do not send `settings.image`; upload `image` for banners.'
example:
heading: 'Our picks'
limit: 8
productIds:
- 14
- 9
properties:
autoplay:
type: boolean
description: ''
example: true
interval:
type: integer
description: 'Must be at least 2000. Must not be greater than 15000.'
example: 1
height:
type: string
description: ''
example: large
enum:
- small
- medium
- large
overlay:
type: string
description: ''
example: dark
enum:
- none
- light
- dark
limit:
type: integer
description: 'Must be at least 4. Must not be greater than 12.'
example: 8
heading:
type: string
description: 'Must not be greater than 80 characters.'
example: 'Our picks'
nullable: true
message:
type: string
description: 'Must not be greater than 120 characters.'
example: 'n'
nullable: true
alt:
type: string
description: 'Must not be greater than 120 characters.'
example: g
nullable: true
link:
type: string
description: 'Must not be greater than 2048 characters.'
example: z
nullable: true
productIds:
type: array
description: ''
example:
- 14
- 9
items:
type: integer
categoryIds:
type: array
description: ''
example:
- 16
items:
type: integer
image:
type: string
description: ''
example: null
delete:
summary: 'Delete store row'
operationId: deleteStoreRow
description: 'Delete a homepage section, its slides, and unused uploaded images.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Row deleted successfully'
data: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Row deleted successfully'
data:
type: array
example: []
tags:
- 'Storefront rows'
parameters:
-
in: path
name: id
description: 'The ID of the row.'
example: 1
required: true
schema:
type: integer
'/api/v1/store/rows/{row_id}/slides':
get:
summary: 'List slides'
operationId: listSlides
description: 'Return slides for one homepage section, ordered by position then id.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Slides retrieved successfully'
data:
-
type: store_slide
id: 3
attributes:
position: 1
title: Summer
subtitle: Sale
image: 'https://api.example.com/storage/store_row/slide.jpg'
link: /products
settings:
cta_text: Shop
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Slides retrieved successfully'
data:
type: array
example:
-
type: store_slide
id: 3
attributes:
position: 1
title: Summer
subtitle: Sale
image: 'https://api.example.com/storage/store_row/slide.jpg'
link: /products
settings:
cta_text: Shop
items:
type: object
properties:
type:
type: string
example: store_slide
id:
type: integer
example: 3
attributes:
type: object
properties:
position:
type: integer
example: 1
title:
type: string
example: Summer
subtitle:
type: string
example: Sale
image:
type: string
example: 'https://api.example.com/storage/store_row/slide.jpg'
link:
type: string
example: /products
settings:
type: object
properties:
cta_text:
type: string
example: Shop
tags:
- 'Storefront slides'
post:
summary: 'Create slide'
operationId: createSlide
description: 'Add a slide to a slider section. Image is required. Non-slider types return 422.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
status: 201
message: 'Slide created successfully'
data:
type: store_slide
id: 3
attributes:
position: 1
title: Summer
image: 'https://api.example.com/storage/store_row/slide.jpg'
link: /products
settings:
cta_text: Shop
properties:
status:
type: integer
example: 201
message:
type: string
example: 'Slide created successfully'
data:
type: object
properties:
type:
type: string
example: store_slide
id:
type: integer
example: 3
attributes:
type: object
properties:
position:
type: integer
example: 1
title:
type: string
example: Summer
image:
type: string
example: 'https://api.example.com/storage/store_row/slide.jpg'
link:
type: string
example: /products
settings:
type: object
properties:
cta_text:
type: string
example: Shop
tags:
- 'Storefront slides'
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
title:
type: string
description: 'Optional slide title, max 80 characters. Must not be greater than 80 characters.'
example: Summer
nullable: true
subtitle:
type: string
description: 'Optional slide subtitle, max 160 characters. Must not be greater than 160 characters.'
example: Sale
nullable: true
link:
type: string
description: 'http(s) URL or a single-leading-slash in-store path. Must not be greater than 2048 characters.'
example: /products
nullable: true
cta_text:
type: string
description: 'Optional button label, max 40 characters. Must not be greater than 40 characters.'
example: Shop
nullable: true
settings:
type: object
description: ''
example: null
properties:
cta_text:
type: string
description: 'Must not be greater than 40 characters.'
example: b
nullable: true
image:
type: string
format: binary
description: 'Required image upload, max 2MB. SVG is sanitized. Must be an image. Must not be greater than 2048 kilobytes.'
required:
- image
parameters:
-
in: path
name: row_id
description: 'The ID of the row.'
example: 1
required: true
schema:
type: integer
-
in: path
name: row
description: 'The homepage section id.'
example: 12
required: true
schema:
type: integer
'/api/v1/store/rows/{row_id}/slides/reorder':
patch:
summary: 'Reorder slides'
operationId: reorderSlides
description: 'Rewrite every slide position for one section. The body must list every current slide of that row exactly once.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Slides reordered successfully'
data: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Slides reordered successfully'
data:
type: array
example: []
tags:
- 'Storefront slides'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
slides:
type: array
description: 'Every current slide of this section exactly once. The server stores dense 1..n in array order. Must have at least 1 items.'
example:
-
id: 3
position: 1
-
id: 4
position: 2
items:
type: object
properties:
id:
type: integer
description: ''
example: 16
position:
type: integer
description: ''
example: 16
required:
- id
- position
required:
- slides
parameters:
-
in: path
name: row_id
description: 'The ID of the row.'
example: 1
required: true
schema:
type: integer
-
in: path
name: row
description: 'The homepage section id.'
example: 12
required: true
schema:
type: integer
'/api/v1/store/slides/{id}':
patch:
summary: 'Update slide'
operationId: updateSlide
description: 'Update slide copy or replace its image. Omitted image is kept. Null may clear title, subtitle, link, and cta_text.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Slide updated successfully'
data:
type: store_slide
id: 3
attributes:
title: Updated
image: 'https://api.example.com/storage/store_row/slide.jpg'
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Slide updated successfully'
data:
type: object
properties:
type:
type: string
example: store_slide
id:
type: integer
example: 3
attributes:
type: object
properties:
title:
type: string
example: Updated
image:
type: string
example: 'https://api.example.com/storage/store_row/slide.jpg'
tags:
- 'Storefront slides'
requestBody:
required: false
content:
multipart/form-data:
schema:
type: object
properties:
title:
type: string
description: 'Slide title. Null clears it. Must not be greater than 80 characters.'
example: Updated
nullable: true
subtitle:
type: string
description: 'Slide subtitle. Null clears it. Must not be greater than 160 characters.'
example: Sale
nullable: true
link:
type: string
description: 'http(s) URL or a single-leading-slash in-store path. Null clears it. Must not be greater than 2048 characters.'
example: /products
nullable: true
cta_text:
type: string
description: 'Button label. Null clears it. Must not be greater than 40 characters.'
example: Shop
nullable: true
settings:
type: object
description: ''
example: null
properties:
cta_text:
type: string
description: 'Must not be greater than 40 characters.'
example: b
nullable: true
image:
type: string
format: binary
description: 'Replacement image. Omit to keep the current file. Null cannot clear image. Must be an image. Must not be greater than 2048 kilobytes.'
delete:
summary: 'Delete slide'
operationId: deleteSlide
description: 'Delete a slide and its unused image after the record is removed.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: 200
message: 'Slide deleted successfully'
data: []
properties:
status:
type: integer
example: 200
message:
type: string
example: 'Slide deleted successfully'
data:
type: array
example: []
tags:
- 'Storefront slides'
parameters:
-
in: path
name: id
description: 'The ID of the slide.'
example: 1
required: true
schema:
type: integer
-
in: path
name: slide
description: 'The slide id.'
example: 3
required: true
schema:
type: integer