{"openapi":"3.1.0","info":{"title":"Rizal Suryawan — Public Content API","version":"1.0.0","description":"Read public portfolio content from any application, including Flutter. No API key or account is required.\n\nJSON is generated at build time and updates with each deployment. Responses can remain fresh in caches for 300 seconds; clients revalidate after that period.\n\nSend ordinary GET requests without credentials or custom headers. Cross-origin GET is supported. Credentialed requests and custom-header preflights are not part of this API.\n\nThere is no pagination, filtering, search, or language query parameter. Query parameters do not change the returned content.\n\nText keeps its original source language. Curated project facts contain both English (en) and Indonesian (id), with verbatim evidence. Missing optional values are null; missing arrays are empty arrays.\n\nWithin v1, clients should tolerate additive properties. Removing fields or changing their types requires a new major version. Provider schemas describe the current exact payload.\n\nBlog responses contain published metadata only. Open each article url in a WebView for its full content, images, and video. Drafts and raw article bodies are excluded.\n\nCheck HTTP status and Content-Type before JSON decoding. Error bodies are not guaranteed to be JSON."},"servers":[{"url":"/"}],"security":[],"tags":[{"name":"Profile","description":"Profile, skills, and professional experience."},{"name":"Projects","description":"Complete curated project information."},{"name":"Contact","description":"Existing public contact information."},{"name":"Blog","description":"Published article metadata for native lists and WebView reading."}],"paths":{"/api/v1/profile.json":{"get":{"operationId":"getProfile","tags":["Profile"],"summary":"Get profile","description":"Profile, about text, hero image, and credentials. Website and image URLs use the configured site origin.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileResponse"},"example":{"data":{"name":"Rizal Suryawan","headline":"Software Engineer","location":"Indonesia","summary":"Software Engineer with 5+ years of experience, currently serving as Principal Engineer, leading technical decisions and building scalable web and mobile applications in remote, async-first environments.","intro":"I build practical, high-impact software across web, mobile, cloud, and AI systems. My work blends full-stack engineering, product-minded execution, and clear ownership in distributed teams.","values":["Own outcomes from architecture to delivery","Keep systems maintainable, observable, and fast","Design interfaces that feel clear and useful","Use AI where it improves real workflows"],"competencies":["Full-stack application architecture","AI agent integration and function calling","Responsive web and mobile interfaces","API design and backend services","Cloud deployment and performance tuning","DevOps, CI/CD, and infrastructure as code"],"websiteUrl":"https://zal.pages.dev","image":{"url":"https://zal.pages.dev/img/hero-img.webp","alt":"Portrait of Rizal Suryawan"},"about":{"heading":"Curious by nature. Still learning.","paragraphs":["Hey, I’m Rizal. I got into coding by tinkering with simple HTML pages and wondering, “what happens if I change this?” That curiosity stuck around. These days, I build things for the web and mobile, and I’m still finding better ways to do it.","I enjoy figuring out how the pieces fit together, from what you see on screen to what happens behind it. I try to keep things simple, useful, and easy for the next person to work on. There’s always something to learn, especially from the people I build with.","Lately, I’ve been exploring AI through small experiments and everyday projects. Away from that, you’ll usually find me tinkering with my Linux setup, sharing a dev video, or looking for a good iced coffee. Some habits are harder to debug than others."]},"credentials":{"provider":"Google Skills","count":59,"url":"https://www.skills.google/public_profiles/45cc37bf-358e-4767-812d-70d754d18a7e","summary":"Hands-on credentials spanning automation, cloud delivery, platform engineering, security, and applied AI.","highlights":[{"title":"Infrastructure as Code","detail":"Terraform on Google Cloud"},{"title":"Containers & Kubernetes","detail":"GKE deployment and operations"},{"title":"Serverless & Event-Driven","detail":"Cloud Run and Eventarc"},{"title":"API Platforms & Secure Delivery","detail":"Apigee X, API Gateway, and DevSecOps"},{"title":"Observability & Cloud Security","detail":"Managed Service for Prometheus, Cloud Observability, and Security Command Center"},{"title":"AI-Enhanced Engineering","detail":"Gemini Code Assist and Google AI Studio"}]}},"meta":{"version":"1"}}}}}}}},"/api/v1/skills.json":{"get":{"operationId":"listSkills","tags":["Profile"],"summary":"List skill groups","description":"Every skill group in curated source order.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkillsResponse"},"example":{"data":[{"name":"Frontend","skills":["SvelteKit","React","TypeScript","TailwindCSS","UI Engineering","Accessibility"]},{"name":"Backend","skills":["Node.js","PHP","Laravel","REST APIs","LLM APIs","Function Calling"]},{"name":"Mobile","skills":["React Native","Mobile Learning","Attendance Systems","Offline-aware UX"]},{"name":"Cloud and Data","skills":["Cloudflare Pages","Google Earth Engine","Performance","Static Sites","Automation"]}],"meta":{"version":"1","total":4}}}}}}}},"/api/v1/experience.json":{"get":{"operationId":"listExperience","tags":["Profile"],"summary":"List experience","description":"Employers and nested roles in source order. Employment periods are human-readable text, not parsed dates.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExperienceResponse"},"example":{"data":[{"company":"CHANGEMAKR ASIA","period":"May 2025 - Present","location":"Singapore / Remote","websiteUrl":"https://changemakr.asia/","roles":[{"position":"Frontend Engineer","period":"May 2025 - August 2025","responsibilities":["Developed responsive, user-friendly interfaces with consistent UI and UX across platforms.","Collaborated with backend teams to integrate APIs and improve the overall user experience."]},{"position":"Full Stack Engineer","period":"August 2025 - January 2026","responsibilities":["Built and maintained scalable web applications across frontend and backend systems.","Delivered product features aligned with requirements while optimizing performance and reliability."]},{"position":"Principal Engineer","period":"February 2026 - Present","responsibilities":["Lead technical direction and architecture for scalable web and mobile applications in a remote, async-first environment.","Drive end-to-end development while improving performance, maintainability, and user experience quality.","Collaborate across teams and improve engineering efficiency through best practices, internal tools, and clear documentation."]}],"technologies":["Architecture","Full-stack","Mobile","Remote Collaboration"]},{"company":"PT Netpolitan Cipta Cakrabuana","period":"October 2025 - Present","location":"Tangerang, Indonesia","websiteUrl":"https://www.netpolitanteam.com/","roles":[{"position":"Full-Stack Developer","period":"October 2025 - Present","responsibilities":["Led end-to-end development of AI-powered LMS features across web and mobile applications.","Designed and implemented system prompts for consistent, context-aware AI agent behavior.","Integrated LLM APIs and function calling to enable intelligent automation and conversational experiences."]}],"technologies":["OpenAI","LLM APIs","LMS","Backend Services","Mobile"]},{"company":"PT TELEXINDO BIZMART","period":"February 2022 - July 2025","location":"Jakarta, Indonesia","websiteUrl":"https://telexindo.com/","roles":[{"position":"Application Developer","period":"February 2022 - July 2025","responsibilities":["Developed and maintained scalable web and mobile applications.","Collaborated with cross-functional teams to ensure consistent UI and UX.","Optimized performance, improved application reliability, and contributed to internal tooling and documentation."]}],"technologies":["PHP","Mobile Apps","Web Apps","Internal Tools"]}],"meta":{"version":"1","total":3}}}}}}}},"/api/v1/projects.json":{"get":{"operationId":"listProjects","tags":["Projects"],"summary":"List projects","description":"Every complete project record in curated source order. Use slug to request a detail record.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectsResponse"},"example":{"data":[{"slug":"agent-client-sdk","title":"Agent Client SDK","description":"Developer-focused SDK for integrating intelligent AI agents into applications with contextual chatbots, function calling, smart search, and personalized assistance.","year":"2026","stack":["TypeScript","AI Agents","OpenAI","SDK"],"featured":true,"githubUrl":null,"demoUrl":"https://www.npmjs.com/package/@rizal_ncc/agent-client","facts":{"capabilities":[{"key":"contextual-chatbots","en":"contextual chatbots","id":"chatbot kontekstual","evidence":"contextual chatbots"},{"key":"function-calling","en":"function calling","id":"function calling","evidence":"function calling"},{"key":"smart-search","en":"smart search","id":"pencarian cerdas","evidence":"smart search"},{"key":"personalized-assistance","en":"personalized assistance","id":"bantuan yang dipersonalisasi","evidence":"personalized assistance"}],"monitoring":[],"audience":{"key":"audience","en":"developers","id":"pengembang aplikasi","evidence":"Developer-focused SDK for integrating intelligent AI agents into applications"}}},{"slug":"ai-agent-service","title":"AI Agent Service","description":"AI platform for BAWANA LMS with learning assistants, AI mentors, function calling, and contextual smart search across course materials.","year":"2025","stack":["OpenAI","LLM APIs","Backend","LMS"],"featured":true,"githubUrl":null,"demoUrl":"https://ncc.bawana.com/","facts":{"capabilities":[{"key":"learning-assistants","en":"learning assistants","id":"asisten pembelajaran","evidence":"learning assistants"},{"key":"ai-mentors","en":"AI mentors","id":"mentor AI","evidence":"AI mentors"},{"key":"function-calling","en":"function calling","id":"function calling","evidence":"function calling"},{"key":"course-search","en":"contextual smart search across course materials","id":"pencarian cerdas kontekstual pada materi kursus","evidence":"contextual smart search across course materials"}],"monitoring":[],"audience":null}},{"slug":"bawana-mobile-learning","title":"Bawana Mobile Learning","description":"AI-integrated mobile learning platform focused on intelligent assistance, personalized learning support, and contextual user interactions.","year":"2025","stack":["Mobile","AI","Learning Platform"],"featured":true,"githubUrl":null,"demoUrl":null,"facts":{"capabilities":[{"key":"intelligent-assistance","en":"intelligent assistance","id":"bantuan cerdas","evidence":"intelligent assistance"},{"key":"learning-support","en":"personalized learning support","id":"dukungan pembelajaran yang dipersonalisasi","evidence":"personalized learning support"},{"key":"contextual-interactions","en":"contextual user interactions","id":"interaksi pengguna yang kontekstual","evidence":"contextual user interactions"}],"monitoring":[],"audience":null}},{"slug":"blue-card-mobile-app","title":"Blue Card Mobile App","description":"Mobile traceability application for recording fisherman harvests, fish ponds, crafts, profits, and blockchain-backed activity records.","year":"2026","stack":["Mobile","Blockchain","Traceability"],"featured":false,"githubUrl":null,"demoUrl":null,"facts":{"capabilities":[{"key":"harvest-recording","en":"recording fisherman harvests","id":"pencatatan hasil panen nelayan","evidence":"recording fisherman harvests"},{"key":"pond-recording","en":"fish ponds","id":"pencatatan kolam ikan","evidence":"fish ponds"},{"key":"craft-recording","en":"crafts","id":"pencatatan crafts","evidence":"crafts"},{"key":"profit-recording","en":"profits","id":"pencatatan keuntungan","evidence":"profits"},{"key":"blockchain-records","en":"blockchain-backed activity records","id":"catatan aktivitas berbasis blockchain","evidence":"blockchain-backed activity records"}],"monitoring":[],"audience":null}},{"slug":"climafund","title":"ClimaFund","description":"Mangrove monitoring and environmental indicators platform using Google Earth Engine to transform satellite data into accessible coastal restoration insights.","year":"2025","stack":["Google Earth Engine","Sustainability","Web App"],"featured":true,"githubUrl":null,"demoUrl":"https://climafund.co/","facts":{"capabilities":[{"key":"mangrove-monitoring","en":"mangrove monitoring","id":"pemantauan mangrove","evidence":"Mangrove monitoring"},{"key":"environmental-indicators","en":"environmental indicators","id":"indikator lingkungan","evidence":"environmental indicators"},{"key":"satellite-insights","en":"transform satellite data into accessible coastal restoration insights","id":"pengolahan data satelit menjadi informasi restorasi pesisir yang mudah diakses","evidence":"transform satellite data into accessible coastal restoration insights"}],"monitoring":["mangrove-monitoring","environmental-indicators"],"audience":null}},{"slug":"quiz-mobile-app-assessment","title":"Quiz Mobile App Assessment","description":"Quiz-based mobile assessment application for PT Telexindo Bizmart employees, built for knowledge checks and lightweight learning workflows.","year":"2024","stack":["Mobile","Assessment","Internal Tools"],"featured":false,"githubUrl":null,"demoUrl":null,"facts":{"capabilities":[{"key":"quiz-assessment","en":"Quiz-based mobile assessment application","id":"asesmen mobile berbasis kuis","evidence":"Quiz-based mobile assessment application"},{"key":"knowledge-checks","en":"knowledge checks","id":"pemeriksaan pengetahuan","evidence":"knowledge checks"},{"key":"learning-workflows","en":"lightweight learning workflows","id":"alur pembelajaran ringan","evidence":"lightweight learning workflows"}],"monitoring":[],"audience":{"key":"audience","en":"PT Telexindo Bizmart employees","id":"karyawan PT Telexindo Bizmart","evidence":"PT Telexindo Bizmart employees"}}},{"slug":"mobile-attendance-app","title":"Mobile Attendance App","description":"Internal attendance mobile application for PT Telexindo Bizmart employees, improving daily attendance workflows with faster and integrated processes.","year":"2024","stack":["Mobile","HR Tech","Internal Tools"],"featured":false,"githubUrl":null,"demoUrl":null,"facts":{"capabilities":[{"key":"attendance","en":"Internal attendance mobile application","id":"pencatatan kehadiran melalui aplikasi mobile internal","evidence":"Internal attendance mobile application"}],"monitoring":[],"audience":{"key":"audience","en":"PT Telexindo Bizmart employees","id":"karyawan PT Telexindo Bizmart","evidence":"PT Telexindo Bizmart employees"}}},{"slug":"telexindo-e-recruitment","title":"Telexindo e-Recruitment","description":"Recruitment platform that digitizes candidate registration and helps HR teams review, organize, and manage applicant data efficiently.","year":"2023","stack":["Web App","Recruitment","PHP"],"featured":false,"githubUrl":null,"demoUrl":"https://recruitment.telexindo.com/","facts":{"capabilities":[{"key":"candidate-registration","en":"digitizes candidate registration","id":"digitalisasi pendaftaran kandidat","evidence":"digitizes candidate registration"},{"key":"applicant-review","en":"review, organize, and manage applicant data","id":"peninjauan, pengorganisasian, dan pengelolaan data pelamar","evidence":"review, organize, and manage applicant data"}],"monitoring":[],"audience":{"key":"audience","en":"HR teams","id":"tim HR","evidence":"HR teams"}}},{"slug":"icca-website","title":"ICCA Website","description":"Official digital interface for the Indonesia Contact Center Association, supporting information access for the contact center community.","year":"2022","stack":["Website","CMS","Frontend"],"featured":false,"githubUrl":null,"demoUrl":"https://icca.co.id/","facts":{"capabilities":[{"key":"information-access","en":"supporting information access for the contact center community","id":"akses informasi untuk komunitas contact center","evidence":"supporting information access for the contact center community"}],"monitoring":[],"audience":{"key":"audience","en":"the contact center community","id":"komunitas contact center","evidence":"the contact center community"}}},{"slug":"telexindo-company-profile","title":"Telexindo Company Profile","description":"Custom PHP-based WordPress theme for a company profile landing page.","year":"2022","stack":["WordPress","PHP","Theme Development"],"featured":false,"githubUrl":null,"demoUrl":"https://telexindo.com/","facts":{"capabilities":[{"key":"company-profile","en":"Custom PHP-based WordPress theme for a company profile landing page","id":"tema WordPress berbasis PHP untuk landing page profil perusahaan","evidence":"Custom PHP-based WordPress theme for a company profile landing page"}],"monitoring":[],"audience":null}}],"meta":{"version":"1","total":10}}}}}}}},"/api/v1/projects/{slug}.json":{"get":{"operationId":"getProject","tags":["Projects"],"summary":"Get a project","description":"Find a complete project by its permanent slug. The record is identical to its collection entry.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"},"example":{"data":{"slug":"agent-client-sdk","title":"Agent Client SDK","description":"Developer-focused SDK for integrating intelligent AI agents into applications with contextual chatbots, function calling, smart search, and personalized assistance.","year":"2026","stack":["TypeScript","AI Agents","OpenAI","SDK"],"featured":true,"githubUrl":null,"demoUrl":"https://www.npmjs.com/package/@rizal_ncc/agent-client","facts":{"capabilities":[{"key":"contextual-chatbots","en":"contextual chatbots","id":"chatbot kontekstual","evidence":"contextual chatbots"},{"key":"function-calling","en":"function calling","id":"function calling","evidence":"function calling"},{"key":"smart-search","en":"smart search","id":"pencarian cerdas","evidence":"smart search"},{"key":"personalized-assistance","en":"personalized assistance","id":"bantuan yang dipersonalisasi","evidence":"personalized assistance"}],"monitoring":[],"audience":{"key":"audience","en":"developers","id":"pengembang aplikasi","evidence":"Developer-focused SDK for integrating intelligent AI agents into applications"}}},"meta":{"version":"1"}}}}},"404":{"description":"Project not found. Static-hosting error bodies may be HTML; check status and content type before decoding JSON."}},"parameters":[{"name":"slug","in":"path","required":true,"description":"Permanent project slug from listProjects. A title change does not change this value.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"example":"agent-client-sdk"}]}},"/api/v1/contact.json":{"get":{"operationId":"getContact","tags":["Contact"],"summary":"Get contact details","description":"Public contact methods, availability, and social links. Email links use mailto; web links are absolute.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"},"example":{"data":{"methods":[{"title":"Phone / WhatsApp","value":"+6285813095235","actionText":"Start chat now","url":"https://wa.me/6285813095235"},{"title":"Email","value":"rizal.suse@gmail.com","actionText":"Send email","url":"mailto:rizal.suse@gmail.com"},{"title":"LinkedIn","value":"Rizal Suryawan","actionText":"Let's connect","url":"https://www.linkedin.com/in/rizal-suryawan/"}],"availability":[{"title":"Response Time","description":"Typically replies within 24 hours"},{"title":"Working Hours","description":"Monday-Friday: 9AM-5PM\nWeekend: Occasionally available"}],"socialLinks":[{"label":"GitHub","url":"https://github.com/zalwan"},{"label":"LinkedIn","url":"https://www.linkedin.com/in/rizal-suryawan/"},{"label":"YouTube","url":"https://youtube.com/@rizalsuryawan86"}]},"meta":{"version":"1"}}}}}}}},"/api/v1/posts.json":{"get":{"operationId":"listPosts","tags":["Blog"],"summary":"List published articles","description":"Published metadata, newest first. Dates include timezone offsets. A null image means no cover is documented. Open url in a WebView.","responses":{"200":{"description":"Public content from the deployed build.","headers":{"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=300, must-revalidate"},"Access-Control-Allow-Origin":{"schema":{"type":"string"},"example":"*"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostsResponse"},"example":{"data":[{"slug":"svelte-5-runes-di-project-nyata","title":"Svelte 5 Runes di Project Nyata","description":"Pelajaran dari migrasi nyata: gotcha reaktivitas, performa, dan pola runes yang tidak dijelaskan lengkap di dokumentasi Svelte 5.","publishedAt":"2026-08-14T00:00:00+07:00","updatedAt":null,"tags":["Svelte","Runes","SvelteKit","Production"],"readingMinutes":6,"image":{"url":"https://zal.pages.dev/images/posts/svelte-5-runes-di-project-nyata.png","alt":"Ilustrasi developer menulis kode Svelte 5 Runes di laptop, dengan diagram reaktivitas di layar","width":null,"height":null},"url":"https://zal.pages.dev/blog/svelte-5-runes-di-project-nyata"},{"slug":"menggunakan-gambar-dalam-artikel-teknis","title":"Menggunakan Gambar dalam Artikel Teknis","description":"Contoh sederhana menggunakan gambar, alt text, dan caption untuk membantu pembaca memahami ide teknis.","publishedAt":"2026-06-16T00:00:00+07:00","updatedAt":null,"tags":["Writing","Documentation","Developer Experience","Visual Communication"],"readingMinutes":3,"image":{"url":"https://zal.pages.dev/images/og/menggunakan-gambar-dalam-artikel-teknis.jpg","alt":"Laptop yang menampilkan diagram arsitektur perangkat lunak dan dokumentasi visual","width":1200,"height":630},"url":"https://zal.pages.dev/blog/menggunakan-gambar-dalam-artikel-teknis"},{"slug":"perjalanan-spiritualitas-programmer-php","title":"Perjalanan Spiritualitas Programmer PHP","description":"Sebuah video tentang perjalanan di balik kehidupan programmer, disajikan melalui obrolan yang lebih santai dan personal.","publishedAt":"2026-06-16T00:00:00+07:00","updatedAt":null,"tags":["Video","PHP","Refleksi","Kehidupan Programmer"],"readingMinutes":2,"image":{"url":"https://zal.pages.dev/images/og/perjalanan-spiritualitas-programmer-php.jpg","alt":"Thumbnail video Perjalanan Spiritualitas Programmer PHP","width":1200,"height":630},"url":"https://zal.pages.dev/blog/perjalanan-spiritualitas-programmer-php"},{"slug":"merancang-ai-agent-yang-siap-production","title":"Merancang AI Agent yang Siap Production","description":"Prinsip praktis untuk membangun AI agent yang terukur, dapat diawasi, dan aman saat menggunakan tools serta function calling.","publishedAt":"2026-06-15T00:00:00+07:00","updatedAt":null,"tags":["AI Agents","Function Calling","Architecture","Production"],"readingMinutes":4,"image":null,"url":"https://zal.pages.dev/blog/merancang-ai-agent-yang-siap-production"},{"slug":"arsitektur-mobile-offline-aware","title":"Membangun Aplikasi Mobile yang Offline-Aware","description":"Pola arsitektur untuk menjaga aplikasi mobile tetap berguna saat jaringan lambat, tidak stabil, atau benar-benar terputus.","publishedAt":"2026-06-13T00:00:00+07:00","updatedAt":null,"tags":["Mobile","Offline First","Architecture","User Experience"],"readingMinutes":4,"image":null,"url":"https://zal.pages.dev/blog/arsitektur-mobile-offline-aware"},{"slug":"puisi-dibulan-juni","title":"Aku Berusaha Membawamu Pulang","description":"Tentang usaha, keterbatasan, dan keberanian untuk tetap berjalan meski tak tahu ujungnya.","publishedAt":"2026-06-12T00:00:00+07:00","updatedAt":null,"tags":["Puisi","Refleksi","Kehidupan","Usaha"],"readingMinutes":3,"image":null,"url":"https://zal.pages.dev/blog/puisi-dibulan-juni"},{"slug":"membuat-typescript-sdk-yang-nyaman-digunakan","title":"Membuat TypeScript SDK yang Nyaman Digunakan","description":"Catatan praktis tentang merancang API client yang sederhana, type-safe, mudah diuji, dan tidak merepotkan pengguna.","publishedAt":"2026-06-11T00:00:00+07:00","updatedAt":null,"tags":["TypeScript","SDK","API Design","Developer Experience"],"readingMinutes":3,"image":null,"url":"https://zal.pages.dev/blog/membuat-typescript-sdk-yang-nyaman-digunakan"},{"slug":"building-this-blog-with-mdsvex","title":"Building This Blog with mdsvex","description":"How this site turns plain Markdown files into fully prerendered, type-safe blog posts on SvelteKit.","publishedAt":"2026-06-10T00:00:00+07:00","updatedAt":"2026-09-30T00:00:00+07:00","tags":["SvelteKit","mdsvex","Markdown"],"readingMinutes":2,"image":null,"url":"https://zal.pages.dev/blog/building-this-blog-with-mdsvex"},{"slug":"menguji-fitur-llm-secara-praktis","title":"Menguji Fitur LLM secara Praktis","description":"Cara sederhana menyusun evaluasi untuk fitur berbasis LLM dengan skenario nyata, rubric yang jelas, dan pemeriksaan otomatis.","publishedAt":"2026-06-09T00:00:00+07:00","updatedAt":null,"tags":["LLM","Testing","Evaluation","AI Engineering"],"readingMinutes":4,"image":null,"url":"https://zal.pages.dev/blog/menguji-fitur-llm-secara-praktis"},{"slug":"debugging-tanpa-terlalu-banyak-menebak","title":"Debugging Tanpa Terlalu Banyak Menebak","description":"Catatan sederhana tentang mempersempit masalah, membaca bukti, dan memperbaiki bug tanpa menambah bug baru.","publishedAt":"2026-06-08T00:00:00+07:00","updatedAt":null,"tags":["Debugging","Engineering","Observability","Problem Solving"],"readingMinutes":3,"image":{"url":"https://zal.pages.dev/images/posts/debugging-tanpa-terlalu-banyak-menebak.jpg","alt":"Ilustrasi developer membaca sinyal debugging dari log, grafik, dan potongan kode","width":1672,"height":941},"url":"https://zal.pages.dev/blog/debugging-tanpa-terlalu-banyak-menebak"},{"slug":"mengubah-data-teknis-menjadi-tampilan-yang-mudah-dipahami","title":"Mengubah Data Teknis Menjadi Tampilan yang Mudah Dipahami","description":"Catatan tentang memilih informasi, visualisasi, dan interaksi ketika data kompleks perlu digunakan oleh orang biasa.","publishedAt":"2026-06-07T00:00:00+07:00","updatedAt":null,"tags":["Data Visualization","UI Engineering","Product","User Experience"],"readingMinutes":4,"image":null,"url":"https://zal.pages.dev/blog/mengubah-data-teknis-menjadi-tampilan-yang-mudah-dipahami"},{"slug":"merancang-api-yang-tidak-mudah-merepotkan","title":"Merancang API yang Tidak Mudah Merepotkan","description":"Beberapa kebiasaan kecil untuk membuat API lebih konsisten, mudah dipahami, dan tetap nyaman saat kebutuhan berubah.","publishedAt":"2026-06-06T00:00:00+07:00","updatedAt":null,"tags":["API Design","Backend","REST","Developer Experience"],"readingMinutes":3,"image":null,"url":"https://zal.pages.dev/blog/merancang-api-yang-tidak-mudah-merepotkan"},{"slug":"menjaga-fitur-tetap-konsisten-di-web-dan-mobile","title":"Menjaga Fitur Tetap Konsisten di Web dan Mobile","description":"Pelajaran praktis ketika satu fitur harus hadir di dua platform dengan kemampuan dan kebiasaan pengguna yang berbeda.","publishedAt":"2026-06-05T00:00:00+07:00","updatedAt":null,"tags":["Web","Mobile","Product Engineering","Architecture"],"readingMinutes":4,"image":null,"url":"https://zal.pages.dev/blog/menjaga-fitur-tetap-konsisten-di-web-dan-mobile"},{"slug":"deploying-sveltekit-to-cloudflare-pages","title":"Deploying SvelteKit to Cloudflare Pages","description":"A lean, fully static deployment pipeline for SvelteKit using the Cloudflare adapter and GitHub Actions.","publishedAt":"2026-05-28T00:00:00+07:00","updatedAt":null,"tags":["Cloudflare","SvelteKit","Deployment"],"readingMinutes":1,"image":null,"url":"https://zal.pages.dev/blog/deploying-sveltekit-to-cloudflare-pages"},{"slug":"svelte-5-runes-migration-notes","title":"Notes on Migrating to Svelte 5 Runes","description":"A practical mapping from Svelte 4 idioms to Svelte 5 runes, and why the compiler stays quiet during the transition.","publishedAt":"2026-05-12T00:00:00+07:00","updatedAt":null,"tags":["Svelte","Runes","Refactoring"],"readingMinutes":1,"image":null,"url":"https://zal.pages.dev/blog/svelte-5-runes-migration-notes"}],"meta":{"version":"1","total":15}}}}}}}}},"components":{"schemas":{"PublicProfile":{"type":"object","properties":{"name":{"type":"string"},"headline":{"type":"string"},"location":{"type":"string"},"summary":{"type":"string"},"intro":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"competencies":{"type":"array","items":{"type":"string"}},"websiteUrl":{"type":"string","format":"uri"},"image":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"alt":{"type":"string"}},"required":["url","alt"],"additionalProperties":false},"about":{"type":"object","properties":{"heading":{"type":"string"},"paragraphs":{"type":"array","items":{"type":"string"}}},"required":["heading","paragraphs"],"additionalProperties":false},"credentials":{"type":"object","properties":{"provider":{"type":"string"},"count":{"type":"integer","minimum":0},"url":{"type":"string","format":"uri"},"summary":{"type":"string"},"highlights":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"detail":{"type":"string"}},"required":["title","detail"],"additionalProperties":false}}},"required":["provider","count","url","summary","highlights"],"additionalProperties":false}},"required":["name","headline","location","summary","intro","values","competencies","websiteUrl","image","about","credentials"],"additionalProperties":false},"PublicSkillGroup":{"type":"object","properties":{"name":{"type":"string"},"skills":{"type":"array","items":{"type":"string"}}},"required":["name","skills"],"additionalProperties":false},"PublicExperience":{"type":"object","properties":{"company":{"type":"string"},"period":{"type":"string"},"location":{"type":"string"},"websiteUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}]},"roles":{"type":"array","items":{"type":"object","properties":{"position":{"type":"string"},"period":{"type":"string"},"responsibilities":{"type":"array","items":{"type":"string"}}},"required":["position","period","responsibilities"],"additionalProperties":false}},"technologies":{"type":"array","items":{"type":"string"}}},"required":["company","period","location","websiteUrl","roles","technologies"],"additionalProperties":false},"PublicFact":{"type":"object","properties":{"key":{"type":"string"},"en":{"type":"string"},"id":{"type":"string"},"evidence":{"type":"string"}},"required":["key","en","id","evidence"],"additionalProperties":false},"PublicProject":{"type":"object","properties":{"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"title":{"type":"string"},"description":{"type":"string"},"year":{"type":"string"},"stack":{"type":"array","items":{"type":"string"}},"featured":{"type":"boolean"},"githubUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}]},"demoUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}]},"facts":{"type":"object","properties":{"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/PublicFact"}},"monitoring":{"type":"array","items":{"type":"string"}},"audience":{"anyOf":[{"$ref":"#/components/schemas/PublicFact"},{"type":"null"}],"description":"Null means the audience is undocumented, not that the project has no users."}},"required":["capabilities","monitoring","audience"],"additionalProperties":false}},"required":["slug","title","description","year","stack","featured","githubUrl","demoUrl","facts"],"additionalProperties":false},"PublicContact":{"type":"object","properties":{"methods":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"value":{"type":"string"},"actionText":{"anyOf":[{"type":"string"},{"type":"null"}]},"url":{"type":"string","format":"uri"}},"required":["title","value","actionText","url"],"additionalProperties":false}},"availability":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"}},"required":["title","description"],"additionalProperties":false}},"socialLinks":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["label","url"],"additionalProperties":false}}},"required":["methods","availability","socialLinks"],"additionalProperties":false},"PublicPost":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"readingMinutes":{"type":"integer","minimum":1},"image":{"anyOf":[{"type":"object","properties":{"url":{"type":"string","format":"uri"},"alt":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}]}},"required":["url","alt","width","height"],"additionalProperties":false},{"type":"null"}]},"url":{"type":"string","format":"uri","description":"Absolute article URL. Open this URL in a WebView to read the article."}},"required":["slug","title","description","publishedAt","updatedAt","tags","readingMinutes","image","url"],"additionalProperties":false},"ProfileResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicProfile"},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"}},"required":["version"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"SkillsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicSkillGroup"}},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"},"total":{"type":"integer","minimum":0}},"required":["version","total"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"ExperienceResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicExperience"}},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"},"total":{"type":"integer","minimum":0}},"required":["version","total"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"ProjectsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicProject"}},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"},"total":{"type":"integer","minimum":0}},"required":["version","total"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"ProjectResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicProject"},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"}},"required":["version"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"ContactResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicContact"},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"}},"required":["version"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false},"PostsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicPost"}},"meta":{"type":"object","properties":{"version":{"type":"string","const":"1"},"total":{"type":"integer","minimum":0}},"required":["version","total"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false}}}}