openapi: 3.0.3
info:
title: 'Yokito Food API API Documentation'
description: 'REST API untuk platform Yokito Food — bakery homemade Padang. Menyediakan endpoint publik (produk, kategori, promo, pesanan) dan endpoint admin terproteksi (CRUD + manajemen pesanan).'
version: 1.0.0
servers:
-
url: 'http://api-yokitofood.test'
tags:
-
name: Authentication
description: "\nLogin, logout, dan info profil admin."
-
name: 'Public — Categories'
description: ''
-
name: 'Public — Products'
description: ''
-
name: 'Public — Promos'
description: ''
-
name: 'Public — Testimonials'
description: ''
-
name: 'Public — Settings'
description: ''
-
name: 'Public — Orders'
description: ''
-
name: 'Admin — Categories'
description: ''
-
name: 'Admin — Products'
description: ''
-
name: 'Admin — Testimonials'
description: ''
-
name: 'Admin — Promos'
description: ''
-
name: 'Admin — Settings'
description: ''
-
name: 'Admin — Orders'
description: ''
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'Login via POST /api/v1/auth/login untuk mendapatkan token. Sertakan sebagai Authorization: Bearer {token} di setiap request admin.'
security:
-
default: []
paths:
/api/v1/auth/login:
post:
summary: 'Login admin'
operationId: loginAdmin
description: 'Mengembalikan Bearer token yang digunakan di semua endpoint admin.'
parameters: []
responses: { }
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Email admin.'
example: admin@yokitofood.test
password:
type: string
description: Password.
example: password
required:
- email
- password
security: []
/api/v1/admin/logout:
post:
summary: Logout
operationId: logout
description: 'Hapus token yang sedang aktif.'
parameters: []
responses: { }
tags:
- Authentication
/api/v1/admin/me:
get:
summary: 'Profil admin yang sedang login'
operationId: profilAdminYangSedangLogin
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Authentication
/api/v1/categories:
get:
summary: 'Daftar kategori aktif'
operationId: daftarKategoriAktif
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Kue Kering'
slug: kue-kering
description: null
sort_order: 1
-
id: 2
name: 'Kue Basah'
slug: kue-basah
description: null
sort_order: 2
-
id: 3
name: Roti
slug: roti
description: null
sort_order: 3
-
id: 4
name: Minuman
slug: minuman
description: null
sort_order: 4
-
id: 5
name: 'Snack Box'
slug: snack-box
description: null
sort_order: 5
properties:
data:
type: array
example:
-
id: 1
name: 'Kue Kering'
slug: kue-kering
description: null
sort_order: 1
-
id: 2
name: 'Kue Basah'
slug: kue-basah
description: null
sort_order: 2
-
id: 3
name: Roti
slug: roti
description: null
sort_order: 3
-
id: 4
name: Minuman
slug: minuman
description: null
sort_order: 4
-
id: 5
name: 'Snack Box'
slug: snack-box
description: null
sort_order: 5
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Kue Kering'
slug:
type: string
example: kue-kering
description:
type: string
example: null
nullable: true
sort_order:
type: integer
example: 1
tags:
- 'Public — Categories'
security: []
/api/v1/products:
get:
summary: 'Daftar produk aktif'
operationId: daftarProdukAktif
description: ''
parameters:
-
in: query
name: category
description: 'Filter berdasarkan slug kategori.'
example: kue-kering
required: false
schema:
type: string
description: 'Filter berdasarkan slug kategori.'
example: kue-kering
-
in: query
name: search
description: 'Cari berdasarkan nama atau deskripsi.'
example: brownies
required: false
schema:
type: string
description: 'Cari berdasarkan nama atau deskripsi.'
example: brownies
-
in: query
name: featured
description: 'Tampilkan hanya produk unggulan.'
example: true
required: false
schema:
type: boolean
description: 'Tampilkan hanya produk unggulan.'
example: true
-
in: query
name: per_page
description: 'Jumlah item per halaman (max 48).'
example: 12
required: false
schema:
type: integer
description: 'Jumlah item per halaman (max 48).'
example: 12
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data: []
links:
first: 'http://api-yokitofood.test/api/v1/products?page=1'
last: 'http://api-yokitofood.test/api/v1/products?page=1'
prev: null
next: null
meta:
current_page: 1
from: null
last_page: 1
links:
-
url: null
label: '« Previous'
page: null
active: false
-
url: 'http://api-yokitofood.test/api/v1/products?page=1'
label: '1'
page: 1
active: true
-
url: null
label: 'Next »'
page: null
active: false
path: 'http://api-yokitofood.test/api/v1/products'
per_page: 12
to: null
total: 0
properties:
data:
type: array
example: []
links:
type: object
properties:
first:
type: string
example: 'http://api-yokitofood.test/api/v1/products?page=1'
last:
type: string
example: 'http://api-yokitofood.test/api/v1/products?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: string
example: null
nullable: true
last_page:
type: integer
example: 1
links:
type: array
example:
-
url: null
label: '« Previous'
page: null
active: false
-
url: 'http://api-yokitofood.test/api/v1/products?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: 'http://api-yokitofood.test/api/v1/products'
per_page:
type: integer
example: 12
to:
type: string
example: null
nullable: true
total:
type: integer
example: 0
tags:
- 'Public — Products'
security: []
'/api/v1/products/{slug}':
get:
summary: 'Detail produk by slug'
operationId: detailProdukBySlug
description: ''
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'No query results for model [App\Models\Product].'
properties:
message:
type: string
example: 'No query results for model [App\Models\Product].'
tags:
- 'Public — Products'
security: []
parameters:
-
in: path
name: slug
description: 'Slug produk.'
example: coklat-brownies-premium
required: true
schema:
type: string
/api/v1/promos/active:
get:
summary: 'Promo yang sedang aktif'
operationId: promoYangSedangAktif
description: 'Mengembalikan promo yang aktif dan dalam periode berlaku.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data: []
properties:
data:
type: array
example: []
tags:
- 'Public — Promos'
security: []
/api/v1/testimonials:
get:
summary: 'Daftar testimoni aktif'
operationId: daftarTestimoniAktif
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data: []
properties:
data:
type: array
example: []
tags:
- 'Public — Testimonials'
security: []
/api/v1/settings:
get:
summary: 'Setting publik (general, contact, social)'
operationId: settingPublikgeneralContactSocial
description: ''
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
general:
site_name: 'Yokito Food'
site_tagline: 'Homemade with Love'
site_description: 'Kue dan makanan rumahan berkualitas.'
contact:
whatsapp_number: '6281234567890'
whatsapp_message: 'Halo, saya ingin memesan: '
email: hello@yokitofood.test
address: ''
social:
instagram: ''
facebook: ''
tiktok: ''
properties:
data:
type: object
properties:
general:
type: object
properties:
site_name:
type: string
example: 'Yokito Food'
site_tagline:
type: string
example: 'Homemade with Love'
site_description:
type: string
example: 'Kue dan makanan rumahan berkualitas.'
contact:
type: object
properties:
whatsapp_number:
type: string
example: '6281234567890'
whatsapp_message:
type: string
example: 'Halo, saya ingin memesan: '
email:
type: string
example: hello@yokitofood.test
address:
type: string
example: ''
social:
type: object
properties:
instagram:
type: string
example: ''
facebook:
type: string
example: ''
tiktok:
type: string
example: ''
tags:
- 'Public — Settings'
security: []
/api/v1/orders:
post:
summary: 'Buat pesanan baru'
operationId: buatPesananBaru
description: "Idempotent via `client_id`: jika client_id sudah ada, kembalikan order yang sama (HTTP 200).\nDiskon promo dihitung otomatis jika kode valid."
parameters: []
responses: { }
tags:
- 'Public — Orders'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
client_id:
type: string
description: 'UUID unik yang dibuat client sebelum request.'
example: 550e8400-e29b-41d4-a716-446655440000
customer_name:
type: string
description: 'Nama pemesan.'
example: 'Rina Sari'
customer_phone:
type: string
description: 'Nomor WhatsApp.'
example: '08123456789'
customer_address:
type: string
description: 'Alamat pengiriman.'
example: 'Jl. Merdeka No. 10, Padang'
nullable: true
notes:
type: string
description: 'Catatan tambahan.'
example: 'Tolong pakai pita merah'
nullable: true
promo_code:
type: string
description: 'Kode promo (opsional).'
example: DISKON10
nullable: true
items:
type: array
description: 'Daftar item pesanan.'
example:
- architecto
items:
type: string
required:
- client_id
- customer_name
- customer_phone
- items
security: []
/api/v1/admin/categories:
get:
summary: ''
operationId: getApiV1AdminCategories
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Categories'
post:
summary: ''
operationId: postApiV1AdminCategories
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Categories'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
description:
type: string
description: ''
example: 'Eius et animi quos velit et.'
nullable: true
is_active:
type: boolean
description: ''
example: false
sort_order:
type: integer
description: 'Must be at least 0.'
example: 60
required:
- name
'/api/v1/admin/categories/{category_id}':
put:
summary: ''
operationId: putApiV1AdminCategoriesCategory_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Categories'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
description:
type: string
description: ''
example: 'Eius et animi quos velit et.'
nullable: true
is_active:
type: boolean
description: ''
example: true
sort_order:
type: integer
description: 'Must be at least 0.'
example: 60
delete:
summary: ''
operationId: deleteApiV1AdminCategoriesCategory_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Categories'
parameters:
-
in: path
name: category_id
description: 'The ID of the category.'
example: 1
required: true
schema:
type: integer
/api/v1/admin/products:
get:
summary: ''
operationId: getApiV1AdminProducts
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Products'
post:
summary: ''
operationId: postApiV1AdminProducts
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Products'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
category_id:
type: string
description: 'The id of an existing record in the categories table.'
example: architecto
name:
type: string
description: 'Must not be greater than 255 characters.'
example: 'n'
description:
type: string
description: ''
example: 'Eius et animi quos velit et.'
nullable: true
price:
type: integer
description: 'Must be at least 0.'
example: 60
stock_status:
type: string
description: ''
example: sold_out
enum:
- available
- sold_out
- pre_order
is_active:
type: boolean
description: ''
example: false
is_featured:
type: boolean
description: ''
example: false
sort_order:
type: integer
description: 'Must be at least 0.'
example: 42
required:
- category_id
- name
- price
- stock_status
'/api/v1/admin/products/{id}':
get:
summary: ''
operationId: getApiV1AdminProductsId
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Products'
post:
summary: ''
operationId: postApiV1AdminProductsId
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Products'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
category_id:
type: string
description: 'The id of an existing record in the categories table.'
example: null
name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
description:
type: string
description: ''
example: 'Eius et animi quos velit et.'
nullable: true
price:
type: integer
description: 'Must be at least 0.'
example: 60
stock_status:
type: string
description: ''
example: available
enum:
- available
- sold_out
- pre_order
is_active:
type: boolean
description: ''
example: false
is_featured:
type: boolean
description: ''
example: true
sort_order:
type: integer
description: 'Must be at least 0.'
example: 42
delete:
summary: ''
operationId: deleteApiV1AdminProductsId
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Products'
parameters:
-
in: path
name: id
description: 'The ID of the product.'
example: 16
required: true
schema:
type: integer
'/api/v1/admin/products/{id}/restore':
post:
summary: ''
operationId: postApiV1AdminProductsIdRestore
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Products'
parameters:
-
in: path
name: id
description: 'The ID of the product.'
example: 16
required: true
schema:
type: integer
/api/v1/admin/testimonials:
get:
summary: ''
operationId: getApiV1AdminTestimonials
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Testimonials'
post:
summary: ''
operationId: postApiV1AdminTestimonials
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Testimonials'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
customer_location:
type: string
description: 'Must not be greater than 255 characters.'
example: 'n'
nullable: true
content:
type: string
description: ''
example: architecto
rating:
type: integer
description: 'Must be at least 1. Must not be greater than 5.'
example: 2
is_active:
type: boolean
description: ''
example: false
sort_order:
type: integer
description: 'Must be at least 0.'
example: 84
required:
- customer_name
- content
'/api/v1/admin/testimonials/{testimonial_id}':
put:
summary: ''
operationId: putApiV1AdminTestimonialsTestimonial_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Testimonials'
delete:
summary: ''
operationId: deleteApiV1AdminTestimonialsTestimonial_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Testimonials'
parameters:
-
in: path
name: testimonial_id
description: 'The ID of the testimonial.'
example: 16
required: true
schema:
type: integer
/api/v1/admin/promos:
get:
summary: ''
operationId: getApiV1AdminPromos
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Promos'
post:
summary: ''
operationId: postApiV1AdminPromos
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Promos'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Must not be greater than 255 characters.'
example: b
code:
type: string
description: 'Must not be greater than 50 characters.'
example: 'n'
nullable: true
description:
type: string
description: ''
example: 'Eius et animi quos velit et.'
nullable: true
type:
type: string
description: ''
example: fixed
enum:
- percentage
- fixed
value:
type: integer
description: 'Must be at least 1.'
example: 16
min_order:
type: integer
description: 'Must be at least 0.'
example: 42
max_discount:
type: integer
description: 'Must be at least 1.'
example: 40
nullable: true
starts_at:
type: string
description: 'Must be a valid date.'
example: '2026-04-10T19:25:31'
nullable: true
ends_at:
type: string
description: 'Must be a valid date. Must be a date after or equal to starts_at.'
example: '2052-05-03'
nullable: true
is_active:
type: boolean
description: ''
example: true
required:
- name
- type
- value
'/api/v1/admin/promos/{promo_id}':
put:
summary: ''
operationId: putApiV1AdminPromosPromo_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Promos'
delete:
summary: ''
operationId: deleteApiV1AdminPromosPromo_id
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Promos'
parameters:
-
in: path
name: promo_id
description: 'The ID of the promo.'
example: 16
required: true
schema:
type: integer
/api/v1/admin/settings:
get:
summary: ''
operationId: getApiV1AdminSettings
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Settings'
put:
summary: ''
operationId: putApiV1AdminSettings
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Settings'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
settings:
type: array
description: ''
example:
- []
items:
type: object
properties:
key:
type: string
description: ''
example: architecto
value:
type: string
description: ''
example: architecto
nullable: true
required:
- key
required:
- settings
/api/v1/admin/orders:
get:
summary: 'Daftar semua pesanan (paginated)'
operationId: daftarSemuaPesananpaginated
description: ''
parameters:
-
in: query
name: status
description: 'Filter status: pending, approved, in_progress, completed, cancelled.'
example: pending
required: false
schema:
type: string
description: 'Filter status: pending, approved, in_progress, completed, cancelled.'
example: pending
-
in: query
name: search
description: 'Cari berdasarkan nomor order, nama, atau telepon.'
example: YKT-2026
required: false
schema:
type: string
description: 'Cari berdasarkan nomor order, nama, atau telepon.'
example: YKT-2026
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Orders'
'/api/v1/admin/orders/{order_id}':
get:
summary: ''
operationId: getApiV1AdminOrdersOrder_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Admin — Orders'
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 16
required: true
schema:
type: integer
'/api/v1/admin/orders/{order_id}/status':
patch:
summary: ''
operationId: patchApiV1AdminOrdersOrder_idStatus
description: ''
parameters: []
responses: { }
tags:
- 'Admin — Orders'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: ''
example: completed
enum:
- approved
- in_progress
- completed
- cancelled
admin_note:
type: string
description: 'Must not be greater than 1000 characters.'
example: b
nullable: true
discount_amount:
type: integer
description: 'Override diskon — admin bisa sesuaikan saat approve. Must be at least 0.'
example: 39
nullable: true
required:
- status
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 16
required: true
schema:
type: integer