# DashForDevs Docs > DashForDevs is ecommerce and business software: a dashboard for owners, a public API (https://papi.dashfordevs.com) and the dash4devs SDK for developers, and DashForDevs Cloud for hosting. These docs are written for business owners, developers and AI agents. Rules for agents: never expose an sk_ secret key (server-side only); never put personal data into public content (blog, page groups, media); create content as drafts unless told to publish; treat 501 not_implemented and 503 not_available_yet as final. Details: https://docs.dashfordevs.com/v1/ai-agents/read-this-first.md --- # What DashForDevs is > One platform to run an online business, and one API to build on it. Source: https://docs.dashfordevs.com/v1/introduction/what-is-dashfordevs · Topic: Start here · Updated: 2026-11-07 [DashForDevs](https://dashfordevs.com) is software for running an online business. You manage products, orders, customers, content and money in one **dashboard**. Your website reads the same data through one **public API**, so the site and the dashboard never disagree. When you need hosting, **DashForDevs Cloud** runs the site and its database next to everything else. It is built by CodeCraft Studios and used by stores that sell everything from baked goods to regulated health products. That is why privacy and security are designed in from the first table, not added later. ## The pieces ```dfd-diagram { "type": "flow", "title": "The DashForDevs platform at a glance", "layout": "LR", "nodes": [ { "id": "owner", "label": "You and your team", "kind": "person", "detail": "Business owners and staff sign in once at accounts.dashfordevs.com." }, { "id": "shopper", "label": "Your customers", "kind": "person", "detail": "People visiting your website. They never see the dashboard." }, { "id": "dashboard", "label": "Dashboard", "kind": "app", "detail": "dashboard.dashfordevs.com: products, orders, customers, marketing, finance." }, { "id": "site", "label": "Your website", "kind": "app", "detail": "Built with any framework. The dash4devs SDK makes it quick in Next.js." }, { "id": "papi", "label": "Public API", "kind": "gateway", "detail": "papi.dashfordevs.com checks your API key and forwards the request." }, { "id": "services", "label": "Platform services", "kind": "service", "detail": "Inventory, orders, customers, content, shipping, payments, analytics and more. Each owns its own data." }, { "id": "cloud", "label": "Cloud", "kind": "infra", "detail": "cloud.dashfordevs.com hosts your site and its database." } ], "edges": [ { "from": "owner", "to": "dashboard", "label": "manages" }, { "from": "shopper", "to": "site", "label": "browses, buys" }, { "from": "site", "to": "papi", "label": "API key" }, { "from": "dashboard", "to": "services", "label": "your session" }, { "from": "papi", "to": "services", "label": "signed identity" }, { "from": "cloud", "to": "site", "label": "hosts", "style": "dashed" } ], "steps": [ { "title": "You run the business", "focus": ["owner", "dashboard", "services"], "text": "Everything you change in the dashboard is saved by the service that owns it: a product by inventory, an order by orders." }, { "title": "Customers visit your site", "focus": ["shopper", "site"], "text": "Your website is yours. It can look like anything." }, { "title": "The site asks the API", "focus": ["site", "papi", "services"], "text": "The site reads products and places orders through the public API with its own key. The API checks the key, then asks the right service." }, { "title": "Cloud hosts it", "focus": ["cloud", "site"], "text": "Optional: deploy the site from GitHub to DashForDevs Cloud, with a managed database." } ] } ``` | Product | Address | What it is for | | --- | --- | --- | | Accounts | accounts.dashfordevs.com | Your one sign-in, two-factor authentication and sessions. | | Hub | hub.dashfordevs.com | Where you land after signing in. Pick an organization and an app. | | Organizations | organizations.dashfordevs.com | Your business profile, team members, roles and billing. | | Dashboard | dashboard.dashfordevs.com | Day-to-day work: products, orders, customers, marketing, finance. | | Public API | papi.dashfordevs.com | The API your website and tools call with an API key. | | SDK | `dash4devs` | A JavaScript library for the public API, plus a build tool. | | Cloud | cloud.dashfordevs.com | Hosting: apps deployed from GitHub, managed Postgres databases. | | Status | status.dashfordevs.com | Whether everything is up, checked every five minutes. | ## What makes it different - **Your data is split by job, not piled in one place.** Each part of the platform (orders, customers, inventory and so on) is its own service with its own database. A problem in one does not spill into the others, and the most sensitive data has the strictest walls. - **Privacy by construction.** Customer details are encrypted at rest. Analytics stores no personal data at all. Payment processors receive order numbers, never what was bought. - **Everything is logged, nothing sensitive is.** Every action is recorded in a tamper-evident log that names which records and fields were touched, never their values. - **One API for every site.** A store built on the SDK keeps working when the platform underneath changes, because the API contract is versioned (`v1`) and kept. ## Next - Running a business? Start with [Your first day](https://docs.dashfordevs.com/v1/business-getting-started). - Building a site or integration? Start with the [Quickstart](https://docs.dashfordevs.com/v1/getting-started). - An AI agent, or connecting one? Read [For AI agents](https://docs.dashfordevs.com/v1/ai-agents). --- # Which docs are for you > Three readers, three paths. Pick yours and skip the rest. Source: https://docs.dashfordevs.com/v1/introduction/choose-your-path · Topic: Start here · Updated: 2026-11-07 These docs are written for three kinds of readers. Every topic is labeled with who it is for, and the menus can filter by it. ## Business owners and staff You want to get work done in the dashboard: add products, ship orders, run a promotion, understand your numbers. No code. - [Your first day](https://docs.dashfordevs.com/v1/business-getting-started): account, organization, team, two-factor. - [Products and stock](https://docs.dashfordevs.com/v1/products): adding and organizing what you sell. - [Orders](https://docs.dashfordevs.com/v1/orders): from a new order to a shipped one. - [Customers and calendar](https://docs.dashfordevs.com/v1/customers) - [Marketing and content](https://docs.dashfordevs.com/v1/marketing): blog, pages, media, discounts. - [Privacy, security and HIPAA](https://docs.dashfordevs.com/v1/privacy-and-security): what we protect and how. The how-tos are step by step, with a screenshot of each screen. ## Developers You are building a storefront, an integration or an internal tool, or deploying to Cloud. - [Quickstart](https://docs.dashfordevs.com/v1/getting-started): your first API call in five minutes. - [How the platform works](https://docs.dashfordevs.com/v1/how-it-works): services, gateways, identity and events, with diagrams you can step through. - [API keys and authentication](https://docs.dashfordevs.com/v1/authentication) - [The dash4devs SDK](https://docs.dashfordevs.com/v1/sdk) - [Storefront API reference](https://docs.dashfordevs.com/v1/api-reference), generated from the gateway's own route table. - [Cloud](https://docs.dashfordevs.com/v1/cloud): apps from GitHub and managed databases. - [Security model](https://docs.dashfordevs.com/v1/security) ## AI agents You are an AI model reading these docs, or you are connecting one (Claude, a coding agent, an automation) to an organization. - [For AI agents](https://docs.dashfordevs.com/v1/ai-agents): the MCP server, its tools, and the rules an agent must follow. - Every page is also plain Markdown: add `.md` to its address. - [/llms.txt](https://docs.dashfordevs.com/llms.txt) lists every page; [/llms-full.txt](https://docs.dashfordevs.com/llms-full.txt) is the whole documentation in one file. --- # Glossary > The words these docs use, in one place. Source: https://docs.dashfordevs.com/v1/introduction/glossary · Topic: Start here · Updated: 2026-11-07 | Term | Meaning | | --- | --- | | **Organization** | Your business on DashForDevs. Products, orders, customers, content and settings all belong to an organization. One person can belong to several. Ids look like `org__…`. | | **Member, role, permission** | A member is a person in an organization. A role (Owner, Admin, Member, or one you create) is a set of permissions such as `org:manage` or `inventory.write`. | | **Dashboard** | dashboard.dashfordevs.com, where an organization's team does day-to-day work. | | **Service** | One part of the platform that owns one kind of data: inventory, orders, customers, content, shipping, payments, billing, analytics, automations, emails, logs. Each has its own database. | | **papi** | papi.dashfordevs.com, the public API your website, apps and tools call with an API key. It checks the key and forwards the request to the right service. | | **tapi** | The private gateway services and the dashboard use to talk to each other. It is not reachable from the internet. | | **Public key (`pk_…`)** | An API key that is safe in browser code. Read-only, and a live one works only from the website addresses (origins) you allow. | | **Secret key (`sk_…`)** | An API key that can change data. Server-side only. Never in a browser, an app bundle or a public repository. | | **Allowed origin** | A website address (for example `shop.example.com`) allowed to use a live public key. | | **SDK (`dash4devs`)** | The JavaScript library for the public API, with React components and a build tool. | | **Page group** | A content type you define (for example Services, Team, FAQs). Each item has the fields you choose. Your site reads them through the API. | | **PHI** | Protected health information: anything that ties a person to their health, including which health product they bought. The platform treats customer data as PHI. | | **BAA** | Business associate agreement: the HIPAA contract between a business and a vendor that handles PHI for it. | | **HIPAA organization** | An organization marked as holding PHI. Stricter rules apply, such as no third-party tools without a BAA. | | **Audit log** | The append-only, hash-chained record every service keeps of what was done, by whom, to which records. It never stores the values. | | **Trace** | One thing a person did (a page view, a save), followed across every service that took part. Shown in the dashboard under Logs. | | **Cloud app, Cloud database** | A website or service deployed from GitHub on DashForDevs Cloud, and a managed Postgres database there. | | **MCP server** | The Model Context Protocol endpoint that lets an AI assistant work with one organization's content, using that organization's secret key. | --- # About CodeCraft Studios > The company behind DashForDevs. Source: https://docs.dashfordevs.com/v1/introduction/about-codecraft-studios · Topic: Start here · Updated: 2026-11-07 ![CodeCraft Studios logo](https://docs.dashfordevs.com/brand/codecraft-studios-logo.svg) CodeCraft Studios is a software development company based in Miami, Florida. We build tools for developers who want to ship faster without giving up control. ## Our story CodeCraft Studios was founded on a simple idea: developers should not have to choose between convenience and control. Every ecommerce platform we tried either gave us a drag-and-drop builder with no flexibility, or a bare API that meant building everything from scratch. We wanted something in between: a platform that handles the hard parts (payments, inventory, shipping, sign-in, security) while letting us build the frontend exactly the way we want. When we could not find it, we built it ourselves. ## What we build Our flagship product is **DashForDevs**: - a dashboard for managing products, orders, customers and everything else; - a developer SDK that makes building storefronts simple; - a public API for when you need direct access; - DashForDevs Cloud, to host the site and its database; - integrations with payment processors, carriers, analytics and email tools. We also build and run storefronts for businesses on our own platform, so every feature is used in production, with real orders, real payments and real customers. ## Our philosophy - **Developer first.** Clean APIs, sensible defaults, no magic. - **Own your code.** Your storefront is your code. No proprietary templates, no themes to fight. - **Ship fast.** The SDK handles the boilerplate so you can focus on what makes your site yours. - **Production ready.** We run DashForDevs ourselves, every day. ## Meet the founder ![John Molina, Founder and CEO of CodeCraft Studios](https://docs.dashfordevs.com/brand/john-molina.webp) John Molina, Founder and CEO of CodeCraft Studios ## The team We are a small, focused team of developers. We do not have a sales team or a customer success department. The engineers who write the code also answer the support tickets. ## Get in touch Questions about your project? Reach out through the [CodeCraft Studios contact page](https://codecraftstudios.net/contact). --- # Are we for you? > An honest answer, before you spend an afternoon finding out. Source: https://docs.dashfordevs.com/v1/introduction/are-we-for-you · Topic: Start here · Updated: 2026-11-07 ## DashForDevs is for you if - you want a **custom website** built with Next.js, React or any framework, and a backend you do not have to build; - you run an **online store**, a **service business**, a **wholesale portal** or a **content site**; - you want **one dashboard** for products, orders, customers, marketing and money; - you care about **privacy and security**, for example because you sell health products; - you would rather use an **API and SDK** than a drag-and-drop builder; - you want **hosting** next to your data, without running servers yourself. ## It may not be for you if - you want a no-code builder like Squarespace or Wix: a DashForDevs site is built by a developer (ours or yours); - you want a ready-made theme that works with no development at all; - you want a marketplace like Etsy or Amazon, where buyers come to the platform rather than to your site. ## Kinds of organizations When you create an organization you pick a type. It tunes the defaults; every feature stays available. | Type | Best for | Leans on | | --- | --- | --- | | Ecommerce | Online stores, retail, direct-to-consumer brands | Products, cart, checkout, shipping, stock, orders | | Service | Agencies, consultants, salons, clinics | Contact forms, appointments, content pages, customers, blog | | Wholesale | B2B, distributors, bulk sellers | Customer pricing, tiers, quantity breaks, wholesale catalogs | ## No developer? CodeCraft Studios builds and runs DashForDevs sites for businesses. See [About CodeCraft Studios](https://docs.dashfordevs.com/v1/introduction/about-codecraft-studios). --- # Create your account > One account signs you in to every DashForDevs product. Source: https://docs.dashfordevs.com/v1/business-getting-started/create-your-account · Topic: Your first day · Updated: 2026-11-07 Your DashForDevs account is yours, not your business's. The same account can belong to several organizations, and it signs you in to every DashForDevs app: the dashboard, Cloud, organizations and the hub. 1. **Open the sign-up page.** Go to [accounts.dashfordevs.com/signup](https://accounts.dashfordevs.com/signup). 2. **Fill in your details.** Your name, email, phone number and a password. Use a password you do not use anywhere else; a password manager makes this easy. 3. **Read and accept the terms.** The terms of service, privacy policy and SMS terms are linked on the form. We record which version you agreed to. 4. **Verify your email.** We email you a six-digit code. Type it in. You cannot join an organization until your email is verified. 5. **Sign in.** You land on the hub at hub.dashfordevs.com, which lists your organizations and the apps you can open. **Already have an account?** Sign in at [accounts.dashfordevs.com/signin](https://accounts.dashfordevs.com/signin). If an app sent you there, you go back to that app afterwards. Otherwise you go to the hub. ## Good to know - **One sign-in, many apps.** When you open the dashboard or Cloud, they check with accounts and let you in without asking again. - **Your session stays on accounts.dashfordevs.com.** No other site, not even another DashForDevs site, ever receives your sign-in cookie. That limits what any single bug elsewhere could do. - **Forgot your password?** Use "Forgot password" on the sign-in page. For your safety, the message is the same whether or not the email exists. - **No "Sign in with Google".** Every account is a DashForDevs account with its own password and, ideally, two-factor sign-in. Next: [Set up your organization](https://docs.dashfordevs.com/v1/business-getting-started/set-up-your-organization). --- # Set up your organization > Your business on DashForDevs, with its profile, settings and team. Source: https://docs.dashfordevs.com/v1/business-getting-started/set-up-your-organization · Topic: Your first day · Updated: 2026-11-07 An **organization** is your business on DashForDevs. Everything you create (products, orders, customers, posts, files) belongs to it, and only its members can see it. ## Create one, or join one - **Starting fresh?** From the hub, create an organization and give it your business name. You become its **Owner**. - **Joining a team?** Ask an Owner or Admin to invite you. You get an email; open the link, sign in (or sign up with the invited address), and accept. ## Fill in the profile Open **organizations.dashfordevs.com** (or **Settings & team** in the dashboard sidebar). There you set: - the business name, logo and contact details your site and emails can show; - the business type and industry, which tune some defaults; - security settings, such as requiring two-factor sign-in for every member; - integrations, custom fields and legal documents. ## Several organizations If you work for more than one business, each is a separate organization with its own data. Switch between them from the hub or the organization switcher at the top of the dashboard. Nothing is ever shared between organizations. **Health products?** If your business handles patient or health data, tell us before you import customers. The organization is then marked as a HIPAA organization and stricter rules apply. See [Privacy, security and HIPAA](https://docs.dashfordevs.com/v1/privacy-and-security). Next: [Invite your team](https://docs.dashfordevs.com/v1/business-getting-started/invite-your-team). --- # Invite your team and set roles > Give each person exactly the access their job needs. Source: https://docs.dashfordevs.com/v1/business-getting-started/invite-your-team · Topic: Your first day · Updated: 2026-11-07 ## Invite someone 1. **Open your team.** In the dashboard sidebar choose **Organization → Settings & team**, which opens organizations.dashfordevs.com. 2. **Invite a member.** Enter their email and pick a role. 3. **They accept.** They get an email with a link. After they sign in and accept, they appear in your member list. ## Roles Every organization starts with three roles. You can add your own. | Role | Can do | | --- | --- | | **Owner** | Everything, including Cloud and billing. An organization always keeps at least one Owner. | | **Admin** | Manage the organization, members, roles, Cloud and logs. | | **Member** | See the organization and its members. Add dashboard areas (below) to let them work. | ### Permissions The organization-level permissions are deliberately few, so you can hold them in your head: | Permission | Lets a person | | --- | --- | | `org:read` | See the organization. | | `org:manage` | Change settings, theme, security, integrations, custom fields and legal documents. | | `members:read` / `members:manage` | See members / invite, remove and change roles. | | `roles:manage` | Create roles and change what they allow. | | `cloud:access` / `cloud:manage` | Open the organization's Cloud / create, change and delete Cloud resources, tokens and billing. | | `logs:read` | See what members did, step by step. | On top of these, each **dashboard area** has _View_, _Edit_ and _Delete_ permissions: inventory, orders, users (customers), discounts, blog, media, analytics, finance, shipping, tax and more. For example, a packer might get `orders.read` and `orders.write` only. **Tip:** give people the least they need. It is easy to add a permission later, and a role that can do less can break less. Next: [Turn on two-factor sign-in](https://docs.dashfordevs.com/v1/business-getting-started/two-factor-sign-in). --- # Turn on two-factor sign-in > A code from your phone, on top of your password. Source: https://docs.dashfordevs.com/v1/business-getting-started/two-factor-sign-in · Topic: Your first day · Updated: 2026-11-07 Two-factor sign-in means a stolen password alone cannot open your account. After your password, you also enter a short code from an authenticator app (or one we email you). ## Turn it on for yourself 1. **Open your security settings.** Go to accounts.dashfordevs.com and open **Security**. 2. **Choose a method.** An authenticator app (\[Google Authenticator\](https://support.google.com/accounts/answer/1066447), \[1Password\](https://1password.com/), Authy and similar) is the strongest. Email codes are the fallback. 3. **Scan the QR code** with your app and type the six-digit code it shows. 4. **Save your recovery codes.** They are shown once. Keep them somewhere safe, offline or in your password manager. Each one gets you in once if you lose your phone. ## Require it for everyone in your organization Owners and Admins can make two-factor sign-in required for the organization, in its security settings. If your organization handles health data, turn this on: it is on the HIPAA checklist at hipaa.dashfordevs.com. - Members who have not set it up get a grace period with a reminder. - After that, they cannot open **this organization** until they set it up. Their account and any other organizations keep working. - The rule is checked for the session itself, not just the account: someone who signed in without the second factor is asked for it before entering the organization. ## If you lose your phone Use a recovery code to sign in, then set up two-factor again on your new phone. If you have no recovery codes left, contact support; we verify who you are before resetting anything, and the reset is recorded. --- # Add a product > From an empty catalog to a product your site can sell, in about five minutes. Source: https://docs.dashfordevs.com/v1/products/add-a-product · Topic: Products and stock · Updated: 2026-11-07 You need the **inventory** edit permission (`inventory.write`). Owners and Admins have it. ## 1\. Create the product 1. **Open Products.** In the dashboard sidebar choose **Commerce → Inventory → Products**. You see every product with its category, brand, status and sizes. ![The Products list in the DashForDevs dashboard, with Bulk Edit, Import and New Product buttons](https://docs.dashfordevs.com/screenshots/products-list.jpg) 2. **Click New Product** at the top right. The New Product form opens. ![The New Product form with Name, Description, Category, Brand and Active](https://docs.dashfordevs.com/screenshots/product-new.jpg) 3. **Fill in the basics.** - **Name**: what customers see, for example "Lemon Loaf". - **Description**: shown on the product page. - **Category** and **Brand**: optional, but they make the catalog easier to browse and filter. Create them under Inventory → Categories and Brands. - **Active**: leave it on to show the product on your site. An inactive product stays in your catalog but is hidden from the storefront. 4. **Click Create product.** You land on the product's own page. The figures at the top show its sizes, stock, price range and when it was created; the tabs below hold everything else. ![A product page with Sizes, In Stock, Price and Created figures above the Information, SEO, Attributes, Sizes, Gallery, QNA, Files, Deals, Subscriptions, API and Analytics tabs](https://docs.dashfordevs.com/screenshots/product-page.jpg) ## 2\. Add sizes and prices A product is sold in one or more **sizes** (for example "2ct" and "5ct", or "100 ml"). If it also has **variations** (flavors, colors), the Sizes tab is a grid: every variation against every size, and each cell has its own price and stock. 1. **Open the Sizes tab.** ![The Size Grid: variations down the side, sizes across the top, each cell with a price and how many are in stock](https://docs.dashfordevs.com/screenshots/product-sizes.jpg) 2. **Add a size.** **Add Size To All** adds a size to every variation at once. 3. **Set prices and stock.** Press a cell to edit that variation's price and stock for that size. Tick a size at the top to rename or archive it on all variations at once. ## 3\. Add images Open the **Gallery** tab and upload photos. The first image is the main one. Images are stored on our CDN and served at the right size for each screen, so they load fast on phones. ## 4\. Make it findable - **SEO tab**: the page title, description and web address (slug) search engines show. Write them for people, not robots. - **Attributes tab**: facts like flavor, material or strength, which your site can use for filters. - **QNA tab**: common questions and answers for the product page. - **Files tab**: documents such as a lab report or a manual. ## The other tabs | Tab | What it is for | | --- | --- | | Information | Name, category, additional categories, brand, description, active. | | Deals | Bundles and quantity breaks for this product. | | Subscriptions | Let customers subscribe and receive it on a schedule. | | API | The product's id and slug, and the API calls that read it, for your developer. | | Analytics | How the product is doing: views and sales over time. | **Changes are live right away.** Your site reads products through the API, so a saved change shows up on the site within a minute or so (sites may cache briefly). **View On Storefront** at the top of the product page opens it on your site. ## Next - [Organize with categories, brands and variations](https://docs.dashfordevs.com/v1/products/organize-your-catalog) - [Keep stock right](https://docs.dashfordevs.com/v1/products/stock-and-locations) - [Edit many products at once](https://docs.dashfordevs.com/v1/products/bulk-edit) --- # Organize with categories, brands and variations > Structure that makes your catalog easy to browse, filter and manage. Source: https://docs.dashfordevs.com/v1/products/organize-your-catalog · Topic: Products and stock · Updated: 2026-11-07 All three live under **Commerce → Inventory** in the dashboard sidebar. ## Categories Categories group products the way customers shop: "Breads", "Cakes", "Gift boxes". They can be nested ("Cakes → Birthday"). Your site can show a page per category and a menu built from them. 1. Open **Inventory → Categories** and click **New category**. 2. Give it a name, an optional parent, an image and a short description. 3. On any product's **Information** tab, pick the category. ## Brands If you sell more than one brand, create them under **Inventory → Brands** and set each product's brand. Customers can filter by brand, and reports can split sales by it. ## Variations Variations are reusable kinds of size or option, such as "Weight" with values 100 g, 250 g and 500 g. Define them once under **Inventory → Variations** and use them on many products, so every product uses the same names and your site can filter consistently. ## Tips - Keep category names short and plain. They become menu labels and page titles. - Avoid creating near-duplicates ("Cake" and "Cakes"). Rename instead: links keep working, because old addresses redirect. - Inactive products keep their category, so reactivating one puts it right back. --- # Keep stock right > How stock is counted, reserved and moved between locations. Source: https://docs.dashfordevs.com/v1/products/stock-and-locations · Topic: Products and stock · Updated: 2026-11-07 Stock is counted **per size**. When a customer checks out, the stock for each size they buy is **reserved** so two people cannot buy the last one at the same time. When the order is placed the reservation becomes a sale; if checkout is abandoned, the reservation is released. ## See and adjust stock Open **Inventory → Stock**. You see every size with its quantity on hand. To correct a count (after a stocktake, a breakage, a delivery), change the number and give a reason. Every change is written to the **movement history**, so you can always see who changed what and why. ## Stock locations (optional) If you keep stock in more than one place (a shop and a warehouse), turn on **Inventory → Stock Locations**: - each location has its own count for every size; - **transfers** move stock from one location to another, and both sides of the move are recorded; - your site can still show one total, or per-location availability. ```dfd-diagram { "type": "flow", "title": "What happens to stock during a sale", "layout": "LR", "nodes": [ { "id": "onhand", "label": "On hand: 10", "kind": "data" }, { "id": "cart", "label": "Checkout started", "kind": "event" }, { "id": "reserved", "label": "Reserved: 2", "kind": "data" }, { "id": "placed", "label": "Order placed", "kind": "event" }, { "id": "sold", "label": "On hand: 8", "kind": "data" }, { "id": "released", "label": "Released back", "kind": "event" } ], "edges": [ { "from": "onhand", "to": "cart" }, { "from": "cart", "to": "reserved", "label": "hold 2" }, { "from": "reserved", "to": "placed", "label": "paid" }, { "from": "placed", "to": "sold" }, { "from": "reserved", "to": "released", "label": "abandoned", "style": "dashed" } ], "steps": [ { "title": "Stock on hand", "focus": ["onhand"], "text": "Ten of this size are in stock." }, { "title": "Checkout reserves", "focus": ["cart", "reserved"], "text": "A customer starts checkout for two. Those two are held, so nobody else can buy them meanwhile." }, { "title": "The order is placed", "focus": ["placed", "sold"], "text": "Payment goes through. The reservation becomes a sale and eight remain." }, { "title": "Or it is abandoned", "focus": ["reserved", "released"], "text": "If checkout is not finished, the two are released back automatically." } ] } ``` ## Restock notifications When a size is out of stock, customers can ask to be told when it is back. See who is waiting under **Inventory → Restock Notifications**. ## Sold **Inventory → Sold** shows what sold, when, in what quantity, which helps you reorder before you run out. --- # Edit many products at once > Bulk edit and import for when one at a time is too slow. Source: https://docs.dashfordevs.com/v1/products/bulk-edit · Topic: Products and stock · Updated: 2026-11-07 ## Bulk Edit 1. Open **Inventory → Products → Bulk Edit**. 2. Filter to the products you want, for example a category or a brand. 3. Change the cells you need: price, stock, active, category. Changed cells are highlighted. 4. Save. Every product you changed is saved, and each change is recorded in the log. ## Import **Inventory → Products → Import** creates or updates products from a spreadsheet. Download the template first, fill it in, then upload it. The import shows you what it will create and change before anything is saved. **Check before you confirm.** An import can change many products in one go. Read the preview; if a column is in the wrong place, every row is wrong. ## Through the API Developers can create and update products from code with a secret key, for example to sync with a supplier. See the [Storefront API reference](https://docs.dashfordevs.com/v1/api-reference). --- # The life of an order > Every stage an order goes through, and what you do at each. Source: https://docs.dashfordevs.com/v1/orders/life-of-an-order · Topic: Orders · Updated: 2026-11-07 ```dfd-diagram { "type": "flow", "title": "From checkout to delivered", "layout": "LR", "nodes": [ { "id": "checkout", "label": "Customer checks out", "kind": "person", "detail": "On your website. Stock is reserved while they pay." }, { "id": "pending", "label": "Pending", "kind": "state", "detail": "Placed and paid, waiting for you. Listed under Orders." }, { "id": "processing", "label": "Processing", "kind": "state", "detail": "You are picking and packing it." }, { "id": "shipped", "label": "Shipped", "kind": "state", "detail": "A label was bought and tracking added. The customer is told." }, { "id": "delivered", "label": "Delivered", "kind": "state", "detail": "Done. It stays in reports and the customer's history." }, { "id": "archived", "label": "Archived", "kind": "state", "detail": "Out of the way, never deleted. See Orders → Archived." } ], "edges": [ { "from": "checkout", "to": "pending" }, { "from": "pending", "to": "processing" }, { "from": "processing", "to": "shipped" }, { "from": "shipped", "to": "delivered" }, { "from": "delivered", "to": "archived", "style": "dashed" } ], "steps": [ { "title": "A new order arrives", "focus": ["checkout", "pending"], "text": "The order appears under Orders with its items, customer and payment." }, { "title": "You prepare it", "focus": ["processing"], "text": "Print a packing slip, pick the items, pack the box." }, { "title": "You ship it", "focus": ["shipped"], "text": "Buy a label from your carrier, or add tracking by hand." }, { "title": "It arrives", "focus": ["delivered", "archived"], "text": "Archive old orders to keep your list short. Nothing is ever lost." } ] } ``` ## Where to find orders | Screen | Use it to | | --- | --- | | **Orders → All orders** | See every order, search, filter by status, open one. | | **Orders → Print orders** | Print packing slips for many orders at once. | | **Orders → Print labels** | Print shipping labels in a batch. | | **Orders → Archived** | Find orders you archived. | | **Orders → From transaction** | Create an order from a payment that has no order (for example, a payment taken by phone). | ## Opening an order An order's page shows the items, quantities and prices, the customer and their address, the payment, any discount used, notes, files and the full history of changes. You can: - change its status; - edit items, prices or the address (every edit is recorded); - add an internal note your team sees and the customer never does; - attach files such as an invoice; - refund all or part of it, when your payment processor supports it. **Who did what?** Every change to an order is recorded with who made it and when. Open **Organization → Logs** to see the steps behind any change. --- # Create an order for a customer > Phone orders, counter sales and custom quotes, made by you in the dashboard. Source: https://docs.dashfordevs.com/v1/orders/create-an-order · Topic: Orders · Updated: 2026-11-07 Use a **draft order** when a customer orders by phone, email or in person, or when you are preparing a custom quote. Nothing is placed until you create it. 1. **Open Orders → All orders** and click **Draft Order**. The Draft Order screen opens. ![The Draft Order screen with Customer, Items, Shipping Address and a Totals panel](https://docs.dashfordevs.com/screenshots/order-new.jpg) 2. **Choose the customer.** Search by name or email. The customer is optional: for a counter sale, leave it empty and fill in the recipient's name or phone; a customer record is created without an email, and nothing is ever sent to it. 3. **Send the invoice elsewhere** (optional): when one person orders and another pays, type the payer's email under **Send the invoice to**. 4. **Add items.** Search products to add them, or use **Custom Item** for something that is not in your catalog. 5. **Add the shipping address.** Pick one of the customer's addresses, or tick **Ship to a different recipient** for a one-off address that is not saved to their profile. 6. **Check the totals.** In the Totals panel set shipping by hand or **From carrier**, add a discount, press **Calculate Tax**, and use **Add Charge** for anything extra. Tick **Credit card fee** to pass the card fee on. 7. **Create the order.** Tick **Email invoice to customer** if you want it sent, then press **Create Order**. It appears in All orders like any other. ## Duplicate an earlier order On any order, choose **Duplicate**. You get a Draft Order with the same items and address, ready to change and create. Handy for repeat customers. --- # Ship an order > Labels, tracking and the states you do not ship to. Source: https://docs.dashfordevs.com/v1/orders/ship-an-order · Topic: Orders · Updated: 2026-11-07 ## Connect your carrier once Open **Organization → Integrations → Shipping**. Connect UPS, FedEx or ShipStation with your own carrier account and choose which one is active. Your carrier credentials are encrypted and are only ever shown masked. For each connection you can set a **default service** (for example Ground) and **per-state overrides** (for example 2-Day to states far away). ## States you do not ship to Under **Commerce → Locations**, list the states you cannot or will not ship to. Checkout on your site refuses those addresses before the customer pays. ## Print labels 1. Open **Orders → Print labels**. 2. Select the orders to ship. 3. Check the service and package for each, then buy and print the labels. 4. Tracking numbers are added to the orders, and the orders move to Shipped. **Health products:** a shipping label names a person, an address and sometimes what is inside. For HIPAA organizations, label buying through carriers waits until those carriers have signed a BAA with us. Until then, buy labels in your carrier's own site and add tracking by hand. --- # Your customers > Everyone who has bought, booked or signed up, in one list. Source: https://docs.dashfordevs.com/v1/customers/your-customers · Topic: Customers and calendar · Updated: 2026-11-07 Open **General → Customers → All customers**. Each customer has a page with their details, addresses, orders, bookings, notes and tags. ## Tags and segments - **Tags** are labels you add by hand: "VIP", "Wholesale", "Do not call". - **Segments** are saved filters that update themselves, for example "spent over $500 in the last 90 days". Manage both under **Customers → Tags & segments**. Use them for discounts, emails and reports. ## Import and export **Customers → Import / export** brings customers in from a spreadsheet or another platform, and exports them when you need a copy. Exports are recorded in the log. ## Bans If someone abuses your store, you can ban an email, phone number or address. Checkout refuses them. ## How customer details are protected Customer details are the most sensitive data you hold, so they are stored apart from everything else: - names, emails, phones, birthdays, addresses, notes and survey answers live in a separate **vault** with its own database and its own encryption keys; - the vault is not reachable from the internet at all, only from the customers service, and it checks your permissions again on every read; - every read is recorded by who, when and which fields, never the values; - searching by email works through a one-way fingerprint, so the email itself is never stored in plain text anywhere. The rest of the platform (orders, analytics, emails) refers to a customer by an id, not by name. --- # Calendar and appointments > Let customers book time with you, on the hours you choose. Source: https://docs.dashfordevs.com/v1/customers/calendar-and-appointments · Topic: Customers and calendar · Updated: 2026-11-07 The calendar is under **Communications → Calendar** in the dashboard. 1. **Set your hours.** In **Calendar → Settings**, choose which days and hours can be booked and how long an appointment is. 2. **Add days off.** In **Calendar → Days off**, block holidays and closures. Nobody can book them. 3. **Let customers book.** Your website shows free times and takes bookings through the API. Customers get a link to reschedule. 4. **Manage appointments.** **Calendar → Appointments** lists every booking. Open one to reschedule, cancel or add notes. Booking details (who booked and why) are stored in the customer vault like every other customer detail. --- # Write a blog post > Write, schedule and publish posts your site shows automatically. Source: https://docs.dashfordevs.com/v1/marketing/write-a-blog-post · Topic: Marketing and content · Updated: 2026-11-07 1. **Open Marketing → Blog → All posts** and click **New post**. 2. **Write.** Title, then the body in the editor: headings, lists, images, links, tables, video embeds from YouTube or Vimeo. 3. **Add a featured image.** It shows on the blog index and when the post is shared. 4. **Pick a category and tags.** Create categories under **Blog → Categories**. 5. **Fill in SEO.** The search title and description. The address (slug) comes from the title; you can change it. 6. **Publish or schedule.** Publish now, schedule a date and time, or keep it as a draft. ## Good to know - **Renaming a post keeps old links working.** When you change a slug, the old address redirects to the new one. - **The editor cleans what you paste.** Formatting is kept; anything that could run code in a reader's browser is removed. - **Blog analytics** (under Blog → Analytics) show views per post without tracking who read them. - **AI can help.** Connect Claude through the [MCP server](https://docs.dashfordevs.com/v1/ai-agents) and it can draft posts straight into your blog as drafts for you to review. --- # Page groups: your own content types > Services, team members, FAQs, locations. Define the fields once, fill them in, your site shows them. Source: https://docs.dashfordevs.com/v1/marketing/page-groups · Topic: Marketing and content · Updated: 2026-11-07 A **page group** is a list of similar things your site shows: services, team members, FAQs, testimonials, locations. You decide the fields; your team fills them in; your site reads them through the API and lays them out. 1. **Open Marketing → Page groups** and create one, for example "Services". 2. **Define its fields**: text, rich text, number, date, link, true/false, image, gallery. 3. **Add items.** Each item gets a title, a web address and the fields you defined, plus named images (for example "hero" and "card"). 4. **Your developer shows them.** In code it is one line: `dash.pageGroup("services").all()`. Change an item and the site updates; no developer needed after the first setup. --- # The media library > Every image, video and file, in folders, served fast. Source: https://docs.dashfordevs.com/v1/marketing/media-library · Topic: Marketing and content · Updated: 2026-11-07 Open **Marketing → Media library**. Upload images, videos and documents, sort them into folders, and reuse them across products, posts and page groups. - **Checked on upload.** A file is identified by its actual contents, not its name, so a disguised file is refused. - **Served from our CDN** at cdn.dashfordevs.com, close to your visitors. - **See where a file is used** before you delete it, so you never break a page by accident. - **Images get responsive sizes**, so phones download small versions. --- # Discounts and promotions > Codes, tiers, buy-one-get-one, customer pricing and a points store. Source: https://docs.dashfordevs.com/v1/marketing/discounts · Topic: Marketing and content · Updated: 2026-11-07 Everything is under **Commerce → Discounts** in the dashboard. | Tool | Use it for | | --- | --- | | **Discount codes** | A code customers type at checkout: amount or percent off, limits, dates, minimum order. | | **Bulk generate** | Hundreds of single-use codes at once, for a campaign or a giveaway. | | **Tiers** | Price levels for groups of customers, such as Wholesale or VIP. | | **Promotions (BOGO)** | Buy X get Y, applied automatically. | | **Customer pricing** | A specific price for a specific customer. | | **Discount store** | Customers spend loyalty points on rewards. | ## Create a discount code 1. Open **Discounts → Discount codes** and click **New**. 2. Type the code (for example SPRING10), choose a percentage or a fixed amount. 3. Set limits if you want: start and end dates, total uses, uses per customer, minimum order, which products. 4. Save. It works at checkout right away. --- # Analytics without tracking people > Visits, sources, funnels and carts, with no personal data stored at all. Source: https://docs.dashfordevs.com/v1/analytics-and-money/analytics · Topic: Analytics, payments and finance · Updated: 2026-11-07 Open **Analytics** in the dashboard sidebar. You see visits, where they came from, which pages they landed on, the checkout funnel, abandoned carts, UTM campaigns and sessions. ## Private by design Most analytics tools keep a record of every person. Ours does not, because for a health store "this person looked at this product" is itself sensitive: - visitors get a **random id that changes every year** and cannot be traced back to a person; - addresses are cut to the path (no query strings), locations to the region, times to the hour; - **only totals are kept**, and any number below 11 is hidden, so a small group cannot be singled out; - for sensitive products, the name is masked in reports (for example `*****57`). You still get the numbers that matter: traffic, conversion, best sellers, campaigns. ## Connected sources Under **Analytics → Connected sources** you can connect [Google Analytics 4](https://support.google.com/analytics/answer/10089681), [Google Search Console](https://search.google.com/search-console/about) and [PostHog](https://posthog.com/docs) and see their numbers here. Those are third parties with their own rules; for a health store, only send them what their agreement with you allows. ## Hound SEO **Hound SEO** in the sidebar checks your site's pages for search problems (titles, descriptions, broken links, speed) and tracks your keywords. --- # Take payments > Connect your processor. Card numbers never touch your site or ours. Source: https://docs.dashfordevs.com/v1/analytics-and-money/payments · Topic: Analytics, payments and finance · Updated: 2026-11-07 ## Connect your processor 1. Open **Organization → Integrations → Payments**. 2. Add your processor account (for example Authorize.net). Start with its **sandbox** keys to test, then switch to live. 3. Run a test order on your site. When it works, switch the connection to live. Your processor credentials are encrypted and only ever shown masked (the last four characters). ## What we never store, and never send - **No card numbers.** The card is turned into a one-time token inside the shopper's browser by the processor's own script. We keep only the processor's ids, the card brand, the last four digits and the expiry. - **No product names to the processor.** The processor sees "Item 1, Item 2" and an order number, never what was bought. For a health store, that keeps purchases private even from the payment company. ## Subscriptions and refunds Subscriptions renew automatically on their schedule. Refunds are made from the order page and sent to the processor. **Not to be confused with your plan.** These are your customers paying you. What you pay DashForDevs is under [Your plan and invoices](https://docs.dashfordevs.com/v1/analytics-and-money/your-plan-and-invoices). --- # Finance and tax > What came in, what went out, and the tax on each sale. Source: https://docs.dashfordevs.com/v1/analytics-and-money/finance-and-tax · Topic: Analytics, payments and finance · Updated: 2026-11-07 ## Finance Under **Finance** in the sidebar: - **Overview**: revenue, refunds, fees and profit over any period; - **Expenses** and the **expense tracker**: what the business spends, by category; - **Integrations**: connect accounting tools. The month-end workbook gathers the period's sales, refunds, tax and expenses into one export for your accountant. ## Tax Under **Finance → Tax**: - **Tax classes** let products be taxed differently (for example food versus general goods); - rates come from tax tables DashForDevs maintains per state, including excise rules, and are re-checked regularly; - checkout, draft orders and order edits all use the same tax engine, so the numbers always agree. We keep the tables current, but you are responsible for your tax filings. Ask your accountant when in doubt. --- # Your plan and invoices > What you pay DashForDevs, and where to see it. Source: https://docs.dashfordevs.com/v1/analytics-and-money/your-plan-and-invoices · Topic: Analytics, payments and finance · Updated: 2026-11-07 Open **Organization → Billing** in the dashboard (it opens organizations.dashfordevs.com). There you find: - your **plan** and what it includes; - **invoices**, each with a PDF, and the option to pay; - **usage**: metered items such as API requests and AI usage; - **payment methods**: cards are entered in Stripe's own secure fields, so we never see the number. We keep only the brand, last four and expiry. ## Cloud is billed separately DashForDevs Cloud has its own billing account, card and monthly invoice, per resource-hour, so hosting costs never mix with your platform plan. See [Cloud billing](https://docs.dashfordevs.com/v1/cloud/billing). --- # How we protect your data > The short version for owners. The technical version is in the security model. Source: https://docs.dashfordevs.com/v1/privacy-and-security/how-we-protect-your-data · Topic: Privacy, security and HIPAA · Updated: 2026-11-07 ## Separate rooms, separate keys Your data is not in one big database. Each kind of data lives with the service that owns it, in its own database: - **customer details** (names, emails, phones, addresses, notes) in a dedicated vault on its own server, with its own encryption keys, unreachable from the internet; - **orders** encrypted with the orders service's own keys; - **payment credentials** and **carrier credentials** encrypted, and only ever shown masked; - **analytics** with no personal data at all. If one part ever had a problem, the walls around the others still stand. ## Only the right people - Everyone signs in through one account system, with two-factor sign-in available to all. - Every action checks the person's role and permissions in your organization, on the server, every time. - DashForDevs staff need two-factor sign-in, always, and their access to your organization is recorded. ## Everything recorded, nothing exposed Every service keeps an **append-only log** where each entry is chained to the one before it, so a deleted or altered entry is detectable. It records who did what to which record and which fields, never the values themselves. You can see your organization's trail under **Organization → Logs**. ## On the wire Every connection is encrypted (HTTPS only). Our public API sits behind a web application firewall that blocks known attacks and floods. ## What you can do - turn on [two-factor sign-in](https://docs.dashfordevs.com/v1/business-getting-started/two-factor-sign-in), and require it for your team; - give each person the [smallest role](https://docs.dashfordevs.com/v1/business-getting-started/invite-your-team) that fits their job; - never share a **secret API key**; if one leaks, rotate it at once (Developers → API keys); - review **Organization → Logs** now and then. --- # HIPAA and health data > If you sell health products or see patients, read this before importing customers. Source: https://docs.dashfordevs.com/v1/privacy-and-security/hipaa · Topic: Privacy, security and HIPAA · Updated: 2026-11-07 If your business sells prescriptions, peptides or other health products, or books patients, the fact that a named person bought a product or booked a visit is **protected health information (PHI)**. [HIPAA](https://www.hhs.gov/hipaa/for-professionals/index.html) sets rules for how it is handled. ## Our side - DashForDevs has a **business associate agreement (BAA)** with [Amazon Web Services](https://aws.amazon.com/compliance/hipaa-compliance/), where the platform runs. - Customer data is treated as PHI everywhere: encrypted at rest, kept in the vault, accessed only by permission, and every access logged. - Payment processors and analytics never receive what a person bought. - **No third-party tool touches PHI without a BAA.** Features that would send customer details to a vendor that has not signed one (some email, SMS and AI features, some carrier label buying) are built but switched off for HIPAA organizations until that vendor signs. ## Your side 1. **Tell us** your organization holds PHI, so it is marked as a HIPAA organization. 2. **Sign a BAA with DashForDevs.** Contact us; it is part of onboarding. 3. **Run your program** at **hipaa.dashfordevs.com**: a checklist of the safeguards HIPAA expects, with automatic checks that read your real settings (for example whether two-factor sign-in is required), and space for your evidence. 4. **Keep PHI out of public content.** Blog posts, page groups and media are public content. Never put patient information in them. **Not legal advice.** We give you the tools and do our part. Whether your business complies is between you, your advisors and HIPAA. --- # Your first API call > A key, one request, real data. Five minutes. Source: https://docs.dashfordevs.com/v1/getting-started/first-api-call · Topic: Quickstart · Updated: 2026-11-07 ## 1\. Get a key In the dashboard, open **Developers → API keys**. Every organization has: - a **public key** (`pk_…`): read-only, safe in browser code; - a **secret key** (`sk_…`): can change data, server-side only. Use a **test** public key for now: test keys work from any origin, including localhost and curl. Live public keys only answer from the [allowed origins](https://docs.dashfordevs.com/v1/authentication/allowed-origins) you list. ## 2\. Call the API ```bash curl "https://papi.dashfordevs.com/api/storefront/products?limit=3" \ -H "X-API-Key: pk_test_YOUR_PUBLIC_KEY" ``` You get JSON back. Useful query parameters: `limit` (up to 100), `page`, `category`, `search`, `in_stock=true`, `sort=price_asc|price_desc|rating`. ```json { "products": [ { "id": "…", "name": "Lemon Loaf", "slug": "lemon-loaf", "…": "…" } ], "pagination": { "total": 42, "limit": 3, "offset": 0, "page": 1, "has_more": true } } ``` ## 3\. Read the headers ```http X-RateLimit-Limit: 120 X-RateLimit-Remaining: 119 X-RateLimit-Reset: 1762531260 Cache-Control: private, no-store, max-age=0, must-revalidate ``` A public key can make 120 requests a minute, a secret key 300. See [Errors and rate limits](https://docs.dashfordevs.com/v1/error-handling). ## What just happened ```dfd-diagram { "type": "sequence", "title": "One storefront request", "actors": [ { "id": "you", "label": "curl" }, { "id": "papi", "label": "papi.dashfordevs.com" }, { "id": "tapi", "label": "tapi (private)" }, { "id": "inv", "label": "inventory service" } ], "messages": [ { "from": "you", "to": "papi", "label": "GET /api/storefront/products X-API-Key" }, { "from": "papi", "to": "papi", "label": "check key, origin, rate limit, organization" }, { "from": "papi", "to": "tapi", "label": "forward + signed identity (no key)" }, { "from": "tapi", "to": "inv", "label": "/inventory/v1/papi/storefront/products" }, { "from": "inv", "to": "you", "label": "200 JSON", "reply": true } ], "steps": [ { "title": "You send the key", "focus": [0], "text": "Only papi ever sees your API key." }, { "title": "papi checks it", "focus": [1], "text": "Is the key real and active? Is the origin allowed? Is the organization active? Under the rate limit?" }, { "title": "papi forwards a signed identity", "focus": [2, 3], "text": "The service receives a short-lived, signed statement of which organization is asking. Never the key." }, { "title": "The service answers", "focus": [4], "text": "inventory reads your organization's products and replies. papi passes the JSON through unchanged." } ] } ``` Next: [Build a storefront page with the SDK](https://docs.dashfordevs.com/v1/getting-started/storefront-with-the-sdk). --- # Build a storefront page with the SDK > A Next.js product list and product page, server-rendered, in a few files. Source: https://docs.dashfordevs.com/v1/getting-started/storefront-with-the-sdk · Topic: Quickstart · Updated: 2026-11-07 ## 1\. Install The SDK is installed from GitHub (ask us for access to the repository): ```bash npm install github:CodeCraft-Studios-LLC/dashfordevs-sdk#v0.3.3 ``` ## 2\. Environment ```bash # .env.local NEXT_PUBLIC_DEVDASH_PUBLIC_KEY=pk_test_YOUR_PUBLIC_KEY DEVDASH_SECRET_KEY=sk_test_YOUR_SECRET_KEY ``` Only variables starting with `NEXT_PUBLIC_` reach the browser. The secret key must never be one of them; the SDK refuses to start if it finds an `sk_` key in a `NEXT_PUBLIC_` variable. ## 3\. Two clients: server and browser ```ts // lib/dash.server.ts: import only from server components and route handlers import "server-only"; import { DashClient } from "dash4devs"; import { assertServerOnlyKey } from "dash4devs/security"; assertServerOnlyKey(process.env.DEVDASH_SECRET_KEY); export const dash = new DashClient({ apiKey: process.env.DEVDASH_SECRET_KEY! }); ``` ```ts // lib/dash.client.ts: safe in the browser import { DashClient } from "dash4devs"; export const dash = new DashClient({ apiKey: process.env.NEXT_PUBLIC_DEVDASH_PUBLIC_KEY! }); ``` ## 4\. A product list ```tsx // app/shop/page.tsx import Link from "next/link"; import { dash } from "@/lib/dash.server"; export default async function Shop() { const { products } = await dash.products.list({ limit: 24, include: ["main_size"] }); return ( ); } ``` ## 5\. A product page ```tsx // app/shop/[slug]/page.tsx import { notFound } from "next/navigation"; import { DashApiError } from "dash4devs"; import { DashImage } from "dash4devs/react"; import { dash } from "@/lib/dash.server"; export default async function Product({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; try { const { product } = await dash.products.getCore(slug); return (

{product.name}

{product.main_image && }
); } catch (e) { if (e instanceof DashApiError && e.status === 404) notFound(); throw e; } } ``` `DashImage` picks the right size from our CDN for each screen. ## 6\. Ship it New to the App Router? The [Next.js documentation](https://nextjs.org/docs/app) covers server components and data fetching in depth. Deploy anywhere that runs Next.js, or on [DashForDevs Cloud](https://docs.dashfordevs.com/v1/cloud) straight from GitHub. Before you go live, switch to **live** keys and add your domain under **Developers → Allowed origins**. Next: [How the platform works](https://docs.dashfordevs.com/v1/how-it-works). --- # Architecture overview > One public door, one private corridor, and a service for each kind of data. Source: https://docs.dashfordevs.com/v1/how-it-works/architecture · Topic: How the platform works · Updated: 2026-11-07 DashForDevs used to be one large application. It is now a set of **services**, each owning one kind of data in its own database, behind two gateways. The public contract (the `/api/...` paths, the keys, the JSON) did not change, so existing sites kept working through the move. ```dfd-diagram { "type": "flow", "title": "Who talks to whom", "layout": "TB", "groups": [ { "id": "public", "label": "Public internet" }, { "id": "edge", "label": "Edge (TLS, firewall)" }, { "id": "corridor", "label": "Private network: the corridor" }, { "id": "private", "label": "Private network: services" }, { "id": "isolated", "label": "Isolated" } ], "nodes": [ { "id": "site", "label": "Your site / SDK", "kind": "client", "group": "public" }, { "id": "browser", "label": "Dashboard user", "kind": "person", "group": "public" }, { "id": "papi", "label": "papi", "kind": "gateway", "group": "edge", "detail": "Public API. Checks API keys, origins, rate limits, organization status. Signs an identity. Never stores business data." }, { "id": "bff", "label": "Dashboard (BFF)", "kind": "app", "group": "edge", "detail": "The dashboard's own server. Holds your session; your browser never holds a token." }, { "id": "accounts", "label": "accounts", "kind": "service", "group": "edge", "detail": "Identity provider: sign-in, two-factor, OIDC." }, { "id": "tapi", "label": "tapi", "kind": "gateway", "group": "corridor", "detail": "Private gateway. Routes //v1/... between services. Not on the internet." }, { "id": "inventory", "label": "inventory", "kind": "service", "group": "private" }, { "id": "orders", "label": "orders", "kind": "service", "group": "private" }, { "id": "customers", "label": "customers", "kind": "service", "group": "private" }, { "id": "vault", "label": "customer PHI vault", "kind": "data", "group": "isolated", "detail": "Only reachable from the customers service. On no gateway." }, { "id": "content", "label": "content", "kind": "service", "group": "private" }, { "id": "more", "label": "8 more services", "kind": "service", "group": "private", "detail": "shipping, payments, billing, emails, analytics, automations, logs and cloud." } ], "edges": [ { "from": "site", "to": "papi", "label": "X-API-Key" }, { "from": "browser", "to": "bff", "label": "session cookie" }, { "from": "bff", "to": "accounts", "label": "OIDC", "style": "dashed" }, { "from": "papi", "to": "tapi", "label": "signed identity" }, { "from": "bff", "to": "tapi", "label": "your token" }, { "from": "tapi", "to": "inventory" }, { "from": "tapi", "to": "orders" }, { "from": "tapi", "to": "customers" }, { "from": "tapi", "to": "content" }, { "from": "tapi", "to": "more" }, { "from": "customers", "to": "vault", "label": "private link" } ], "steps": [ { "title": "Two kinds of callers", "focus": ["site", "browser"], "text": "Websites and tools call with an API key. People use the dashboard with a signed-in session." }, { "title": "Two front doors", "focus": ["papi", "bff", "accounts"], "text": "Keys go to papi. Sessions go to the dashboard's own server, which signs people in through accounts." }, { "title": "One private corridor", "focus": ["tapi"], "text": "Both doors reach services only through tapi, on the private network." }, { "title": "Services own their data", "focus": ["inventory", "orders", "customers", "content", "more"], "text": "Each service has its own database and its own audit log. No service reads another's tables." }, { "title": "The vault", "focus": ["customers", "vault"], "text": "Customer personal data sits in a vault that only the customers service can reach." } ] } ``` ## The services | Service | Owns | Personal data? | | --- | --- | --- | | accounts | People, sign-in, two-factor, sessions, OIDC | Account holders only | | organizations | Organizations, members, roles, permissions, compliance checklist | No | | inventory | Catalog, sizes, stock, locations, reviews, bundles | No | | orders | Orders, carts, checkout, discounts, loyalty, tax, finance | Yes, encrypted | | customers | Shoppers, tags, segments, bans, calendar | Ids only; details in the vault | | customer PHI vault | Customer names, contacts, addresses, notes, survey answers | Yes, encrypted, isolated | | content | Blog, page groups, media, legal documents, docs | No | | shipping | Carrier connections, label rules, shipping locations | Credentials, encrypted | | payments | Stores' payment processors, charges, subscriptions | Card tokens only, never card numbers | | billing | What DashForDevs charges organizations | No | | emails | Inbox, contact forms, transactional mail | Yes | | analytics | De-identified reports | None, by construction | | automations | Flows, runs, webhooks | Ids only at rest | | logs | Traces of every action, per organization | Field names only | | cloud | Apps, databases, projects for DashForDevs Cloud | No | ## Rules every service follows - **One writer per table.** Exactly one service writes each kind of data. - **Organization from identity, never from the request.** A service takes the organization from the signed identity or the verified session, never from a header or parameter the caller chose. - **Two checks.** The gateway checks the caller, and the service checks again. One bug is not a breach. - **Ids, not values, in logs and events.** Audit rows, traces and events carry record ids and field names only. - **Postgres only**, one database per service, on AWS, encrypted at rest. --- # Identity and sign-in > How people prove who they are, and how every app trusts that. Source: https://docs.dashfordevs.com/v1/how-it-works/identity-and-sign-in · Topic: How the platform works · Updated: 2026-11-07 **accounts.dashfordevs.com** is the platform's identity provider. Every other app (hub, dashboard, organizations, Cloud, admin) is a relying party that signs people in through it with [OpenID Connect](https://openid.net/developers/how-connect-works/). ```dfd-diagram { "type": "sequence", "title": "Signing in to the dashboard", "actors": [ { "id": "user", "label": "Browser" }, { "id": "dash", "label": "Dashboard server" }, { "id": "acc", "label": "accounts" } ], "messages": [ { "from": "user", "to": "dash", "label": "open dashboard" }, { "from": "dash", "to": "user", "label": "redirect to accounts (PKCE)", "reply": true }, { "from": "user", "to": "acc", "label": "password + second factor" }, { "from": "acc", "to": "user", "label": "redirect back with a one-time code", "reply": true }, { "from": "user", "to": "dash", "label": "code" }, { "from": "dash", "to": "acc", "label": "exchange code (client secret)" }, { "from": "acc", "to": "dash", "label": "access + refresh tokens", "reply": true }, { "from": "dash", "to": "user", "label": "encrypted session cookie for this host only", "reply": true } ], "steps": [ { "title": "Go sign in", "focus": [0, 1], "text": "The dashboard sends the browser to accounts with a PKCE challenge." }, { "title": "Prove it", "focus": [2, 3], "text": "Password, then the second factor if enabled. accounts returns a one-time code." }, { "title": "Server-side exchange", "focus": [4, 5, 6], "text": "The dashboard's server, not the browser, exchanges the code with its client secret." }, { "title": "No tokens in the browser", "focus": [7], "text": "The browser only gets an httpOnly, encrypted cookie for dashboard.dashfordevs.com. Tokens stay on the server." } ] } ``` ## The rules - **Access tokens live 10 minutes.** Refresh tokens live 30 days and **rotate** on every use. Reusing an old refresh token revokes the whole family, the signal that one was stolen. - **Tokens are signed with RS256**; public keys are at `/.well-known/jwks.json`. - **No token ever reaches browser JavaScript.** Every app is a confidential client with a server that holds the tokens. - **The sign-in cookie lives on accounts.dashfordevs.com only**, never on `.dashfordevs.com`, so a bug on another subdomain cannot steal it. - **The `amr` claim** says how this session authenticated (for example `["pwd", "otp"]`). Sensitive actions check that the session actually passed two-factor, not just that the account has it on. - **`?next=` is allowlisted.** After sign-in you return to the app that sent you, if it is a known DashForDevs app; otherwise you land on the hub. - **Staff always need two-factor**, independent of any organization's policy. ## Endpoints ``` GET /.well-known/openid-configuration GET /.well-known/jwks.json GET /oauth/authorize POST /oauth/token GET /oauth/userinfo POST /oauth/revoke ``` These are for DashForDevs' own apps. Storefronts do not use OIDC; they use [API keys](https://docs.dashfordevs.com/v1/authentication). --- # Events, traces and audit logs > How services tell each other what happened, and how every action is recorded. Source: https://docs.dashfordevs.com/v1/how-it-works/events-and-logs · Topic: How the platform works · Updated: 2026-11-07 ## Events: the outbox When something happens in one service that another needs to know (an order is placed, so inventory must turn a reservation into a sale), the first service **publishes an event**. ```dfd-diagram { "type": "flow", "title": "An event from orders to inventory", "layout": "LR", "nodes": [ { "id": "tx", "label": "orders: save order", "kind": "service", "detail": "The order row and the outbox row are written in the SAME database transaction." }, { "id": "outbox", "label": "outbox row", "kind": "data", "detail": "If the order rolls back, so does the event. Nothing is ever sent for a change that did not happen." }, { "id": "dispatch", "label": "dispatcher", "kind": "service", "detail": "After commit, delivers the event. Retries with backoff, keeps order per record, dead-letters after repeated failure." }, { "id": "tapi", "label": "tapi", "kind": "gateway" }, { "id": "inv", "label": "inventory: /internal/events/", "kind": "service", "detail": "Verifies the HMAC signature, refuses replays, applies each event exactly once (by event id)." } ], "edges": [ { "from": "tx", "to": "outbox", "label": "same transaction" }, { "from": "outbox", "to": "dispatch", "label": "after commit" }, { "from": "dispatch", "to": "tapi", "label": "signed request" }, { "from": "tapi", "to": "inv" } ], "steps": [ { "title": "Write once", "focus": ["tx", "outbox"], "text": "The change and the event are one transaction." }, { "title": "Deliver after commit", "focus": ["dispatch"], "text": "A dispatcher sends it, retrying until it is acknowledged." }, { "title": "Signed, through the gateway", "focus": ["tapi"], "text": "Every service-to-service call is signed with a pairwise key, and goes through tapi like everything else." }, { "title": "Applied exactly once", "focus": ["inv"], "text": "The receiver checks the signature, ignores replays, and remembers which events it already applied." } ] } ``` Events carry **ids and amounts, never personal data**. A receiver that needs details asks the owning service for them, with permission checks. ## Traces Every action a person takes (opening a page, saving a product) is a **trace**. Every hop that served it (the dashboard, each service, any service that one called) sends one span: the route template, method, status, timing, who, which organization, and **which records and field names** were touched. Never a value. Members with `logs:read` see their organization's traces under **Organization → Logs**. ## Audit logs Each service keeps its own **append-only, hash-chained audit log**. Every entry includes a hash of the previous one, so removing or editing an entry breaks the chain, and the chains are verified. Audit entries record ids and field names, never values. Staff can read every chain from the admin console; each staff read is itself audited. --- # Versioning and compatibility > What v1 promises, and how changes reach you without breaking you. Source: https://docs.dashfordevs.com/v1/how-it-works/versioning · Topic: How the platform works · Updated: 2026-11-07 The public API and these docs are **v1**. Each service's API lives under `//v1/`, and the storefront paths under `/api/...` are the v1 contract the SDK is built on. ## What we never do within v1 - remove an endpoint, a field or an accepted value; - rename a field or change its type; - change what a status code or error code means; - make an optional parameter required. ## What we may do within v1 - add endpoints, optional parameters and response fields; - add new error codes for new situations; - add values to lists your code should treat as open-ended. Write a **tolerant reader**: ignore fields you do not know, and do not depend on the order of keys. ## When something must change A breaking change ships as a new version (`v2`) beside the old one. The old version keeps working through a published deprecation period, responses carry a `Deprecation` header that links to the migration guide, and these docs keep every published version readable with a banner pointing to the current one. --- # Public and secret keys > The single most important page for anyone shipping a site. Source: https://docs.dashfordevs.com/v1/authentication/public-and-secret-keys · Topic: API keys and authentication · Updated: 2026-11-07 **A secret key (`sk_…`) never goes in browser code.** Not in a `NEXT_PUBLIC_` variable, not in a mobile app, not in a public repository. Anyone who has it can change your organization's data. Every API key is a pair: a public half and a secret half. | | Public key `pk_…` | Secret key `sk_…` | | --- | --- | --- | | Where it may live | Browser bundles, apps, anywhere | Your server only | | Can read storefront data | Yes | Yes | | Can change data (admin API) | **No** | Yes | | Origin check (live keys) | Yes: only from your allowed origins | No | | Rate limit | 120 requests per minute | 300 requests per minute | | Typical user | Your website's pages | Your server, sync jobs, the MCP server | Send either in the `X-API-Key` header: ```bash curl https://papi.dashfordevs.com/api/storefront/products -H "X-API-Key: pk_live_…" ``` ## Test and live keys - **Test keys** skip the origin check, so they work from localhost, curl and CI. Use them while building. - **Live keys** enforce HTTPS and your allowed origins for the public half. ## How papi checks a request In this order, each one stopping the request with a clear error: 1. Is there a key, and does it look like one? (`401 api_key_required`, `401 invalid_api_key_format`) 2. Does it exist? (`401 invalid_api_key`) 3. Is it active? (`403 api_key_inactive`) 4. Is the organization active? (`403 organization_shutdown`) 5. For a live public key: is the origin allowed and the connection HTTPS? (`403 origin_not_allowed`) 6. For admin routes: is it a secret key? (`403 secret_key_required`) 7. Under the rate limit? (`429`) Then papi forwards the request with a short-lived **signed identity**. The services behind it never see your key, and check the identity again themselves. ## Keys in the SDK The SDK protects you from the most common mistake: it **throws** if it is created with an `sk_` key in a browser, or if any `NEXT_PUBLIC_*` variable holds a secret key (it names the variable, never the value). Use `assertServerOnlyKey` from `dash4devs/security` in your server-only module. --- # Allowed origins > Which websites may use your live public key. Source: https://docs.dashfordevs.com/v1/authentication/allowed-origins · Topic: API keys and authentication · Updated: 2026-11-07 A live public key answers only requests whose `Origin` (or `Referer`) is on its list. Manage the list in the dashboard under **Developers → Allowed origins**. ## Patterns | Pattern | Matches | | --- | --- | | `shop.example.com` | exactly that host | | `*.example.com` | `example.com` and every subdomain | | `localhost`, `127.0.0.1` | always allowed, no entry needed | **Enter bare hosts, not URLs.** `shop.example.com` works; `https://shop.example.com` or `shop.example.com/` never matches. ## Server-side requests Requests from your server (a Next.js server component, for example) have no browser `Origin`. Use the **secret key** there; secret keys skip the origin check. Do not try to fake an origin with a public key: it works until it does not, and the secret key is the right tool. ## Is this security? The origin check stops other websites from casually using your public key from their visitors' browsers. It is not a lock against a determined script, which is exactly why public keys are read-only. --- # Rotating keys, and what to do when one leaks > Rotation without downtime, and a five-minute leak checklist. Source: https://docs.dashfordevs.com/v1/authentication/rotate-and-leaks · Topic: API keys and authentication · Updated: 2026-11-07 ## Rotate without downtime An organization can have more than one key, so you can switch with no gap: 1. **Create a second key** under **Developers → API keys**, with the same allowed origins. 2. **Deploy your site with the new key.** 3. **Watch** **Developers → Request logs** until the old key shows no traffic. 4. **Delete the old key.** Creating, regenerating, revealing and deleting keys all require that your session passed two-factor sign-in, and each is recorded. ## A secret key leaked You pushed it to GitHub, pasted it in a chat, shipped it in a bundle. Do this now, in order: 1. **Regenerate it.** **Developers → API keys**, open the key, **Regenerate**. The old public and secret halves stop working on the next request. This is the step that matters; do it first. 2. **Put the new secret where it belongs**: your server's environment variables, then redeploy. 3. **Check what happened.** **Developers → Request logs** shows traffic per key and route, and **Organization → Logs** shows every change made. Look for writes you did not make. 4. **Clean up the leak** (remove the commit, delete the message), knowing the old key is already dead. 5. **Tell us** if you see changes you did not make. A leaked **public** key is not an emergency: it is read-only and origin-locked. Regenerate it when convenient. --- # Outgoing webhooks > We call your URL when something happens. Here is how to trust the call. Source: https://docs.dashfordevs.com/v1/webhooks/outgoing-webhooks · Topic: Automations and webhooks · Updated: 2026-11-07 Add an endpoint in the dashboard's automations section, choose the events, and we send a signed `POST` to your URL each time one happens. ## Events | Event | When | | --- | --- | | `contact_form.submitted` | A contact form was submitted. | | `contact_form.replied` | Staff replied, or the sender replied by email. | | `contact_form.status_changed` | A submission's status changed. | | `event.scheduled` | A calendar booking was created. | | `email.received`, `email.sent`, `email.delivered`, `email.bounced` | Email activity. | ## The request ```http POST /your/endpoint HTTP/1.1 Content-Type: application/json User-Agent: Dash4Devs-Webhooks/1.0 X-Dash4Devs-Event: contact_form.submitted X-Dash4Devs-Delivery: … X-Dash4Devs-Signature: t=1762531200,v1=5f2b… {"created_at":"2026-11-07T15:00:00Z","data":{"submission_id":"…"},"event":"contact_form.submitted","id":"evt_…","organization_id":"org__…"} ``` **`data` holds ids, not personal details.** Fetch what you need through the API with your own key. That keeps names and messages out of your logs and ours. ## Verify the signature The signature is [HMAC](https://datatracker.ietf.org/doc/html/rfc2104)\-SHA256 of `"."` with your secret, hex-encoded. Always verify against the **raw body bytes**, compare in constant time, and reject old timestamps. ```js // Node.js import crypto from "node:crypto"; export function verify(rawBody, header, secret, toleranceSec = 300) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const t = Number(parts.t); if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest("hex"); const given = Buffer.from(parts.v1 ?? "", "hex"); const want = Buffer.from(expected, "hex"); return given.length === want.length && crypto.timingSafeEqual(given, want); } ``` ```python # Python import hashlib, hmac, time def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) t = int(parts.get("t", "0")) if abs(time.time() - t) > tolerance: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts.get("v1", "")) ``` ## Retries - Up to **5 attempts**: immediately, then after 30 seconds, 3 minutes, 15 minutes and 1 hour. - Any `2xx` is success. Answer quickly and do slow work afterwards. - `X-Dash4Devs-Delivery` is the same on every retry: use it to ignore duplicates. - After 25 failures in a row the endpoint is switched off; turn it back on in the dashboard. ## Safety rules we enforce - **HTTPS only.** - URLs that resolve to private, loopback or link-local addresses are refused, every attempt. - Redirects are never followed. - Secrets are shown masked after creation. --- # Flows > When this happens, do that. Built visually, run reliably. Source: https://docs.dashfordevs.com/v1/webhooks/flows · Topic: Automations and webhooks · Updated: 2026-11-07 A **flow** is a trigger followed by steps, drawn as a graph in the dashboard under **Automations**: - **Trigger**: something happened (an order was placed, a form was submitted, a webhook arrived, a schedule fired). - **Actions**: send an email, call a webhook, tag a customer, wait. - **Conditions**: branch on a value (order total over $100, customer has tag VIP). - **Delays and waits**: pause for a time, or until something else happens. Start from a built-in template or a blank canvas, switch the flow on, and every **run** appears in its history with each step's outcome. ## How runs are kept safe - Runs execute in a separate worker, so a slow flow never slows the dashboard. - Run, step and log records hold **ids and field names only**. When a run is waiting, its working data is sealed (encrypted) and deleted when the run ends. - Steps that would send personal details to a vendor without a BAA (for example SMS or AI providers) are switched off for HIPAA organizations. ## Inbound webhooks A flow can start from an inbound webhook: you get a URL and a secret. Calls must carry `X-Dash4Devs-Signature: t=,v1=` computed exactly as for [outgoing webhooks](https://docs.dashfordevs.com/v1/webhooks/outgoing-webhooks). --- # Error responses > One shape, stable codes, and what to do about each. Source: https://docs.dashfordevs.com/v1/error-handling/errors · Topic: Errors and rate limits · Updated: 2026-11-07 Errors are JSON with a machine-readable `error` and a human `message`: ```json { "error": "origin_not_allowed", "message": "Origin 'https://other.site' is not in the allowed origins list" } ``` Branch on `error` (and the status), never on `message`: messages may be reworded, codes never are within v1. ## Gateway errors | Status | `error` | Meaning | What to do | | --- | --- | --- | --- | | 401 | `api_key_required` | No `X-API-Key` header. | Send the key. | | 401 | `invalid_api_key_format` | The value is not a key. | Check for whitespace or a truncated value. | | 401 | `invalid_api_key` | No such key. | It was deleted or regenerated. Use the current one. | | 403 | `api_key_inactive` | The key is switched off. | Turn it on, or use another. | | 403 | `organization_shutdown` | The organization is shut down. Includes `shutdown_at`. | Contact support. | | 403 | `origin_not_allowed` | Live public key from an origin not on its list. | Add the host under Allowed origins (bare host, no https://). | | 403 | `secret_key_required` | An admin route called with a public key. | Call it from your server with the secret key. | | 405 | (empty body) | Wrong method for this path. | See the `Allow` header. | | 429 | `Rate limit exceeded` | Too many requests this minute. | Wait `retry_after` seconds. | | 501 | `not_implemented` | The route exists in v1 but is not built on the new platform yet. Includes `"available": false`. | Do not retry. | | 503 | `not_available_yet` | The route is held back for this organization (for example, customer data for a HIPAA organization whose HIPAA path is not complete). Includes `"available": false` and a `reason`. | Do not retry. | Service errors (a product not found, a validation problem) use the same shape, for example `404 {"error": "not_found", "message": "Product not found"}` or `400 {"error": "validation_error", "message": "name is required"}`. ## In the SDK Every failure is a `DashApiError` with `status`, `code` (the `error` value, or `network_error`, `timeout`, `aborted`, `invalid_response`), `fields` (for validation errors), `requestId` and `retryable`. GET requests retry automatically on network errors, timeouts, 408, 425, 429, 502, 503 and 504, honoring `Retry-After`. Writes are never retried automatically. --- # Rate limits > Per key, per minute, with headers that tell you where you stand. Source: https://docs.dashfordevs.com/v1/error-handling/rate-limits · Topic: Errors and rate limits · Updated: 2026-11-07 Limits are counted per minute in a fixed window. | Caller | Limit per minute | | --- | --- | | Public key (`pk_`) | 120 | | Secret key (`sk_`) | 300 | | Signed-in shopper session (Bearer token) | 1200 | | No key (by IP address) | 30 | Every `/api/` response carries: ```http X-RateLimit-Limit: 120 X-RateLimit-Remaining: 87 X-RateLimit-Reset: 1762531260 ``` Over the limit you get `429` with a `Retry-After` header: ```json { "error": "Rate limit exceeded", "message": "…", "retry_after": 23 } ``` ## Staying under it - **Cache on your server.** Product and content reads change rarely; the SDK caches anonymous GETs in Next.js for 60 seconds by default (`DASH4DEVS_REVALIDATE`). - **Render on the server** with the secret key, rather than calling from every visitor's browser. - **Batch** where an endpoint accepts several ids. A firewall in front of the API also applies a coarse per-IP and per-key ceiling above these numbers, to absorb floods. --- # Install and configure > One client, a handful of options, sensible defaults. Source: https://docs.dashfordevs.com/v1/sdk/install-and-configure · Topic: The dash4devs SDK · Updated: 2026-11-07 ```bash npm install github:CodeCraft-Studios-LLC/dashfordevs-sdk#v0.3.3 ``` The package ships compiled ES modules and is safe to import on the server and in the browser (every browser API is guarded). No `transpilePackages` needed in Next.js. ```ts import { DashClient } from "dash4devs"; const dash = new DashClient({ apiKey: process.env.NEXT_PUBLIC_DEVDASH_PUBLIC_KEY! }); ``` ## Options | Option | Default | What it does | | --- | --- | --- | | `apiKey` | required | A `pk_` key in the browser, a `sk_` key on the server. | | `baseURL` | `https://papi.dashfordevs.com` | The API host. A localhost URL is kept for local development. | | `timeoutMs` | `15000` | Per-request timeout. `0` disables it. | | `retries` | `2` | Retries for GET and HEAD on network errors, timeouts and 408, 425, 429, 502, 503, 504. Writes never retry. | | `analytics` | private | First-party analytics is de-identified by default. | ## Caching in Next.js In production builds, anonymous GETs use the Next.js data cache with `revalidate: 60` and the tag `dash4devs`. Change the window with `DASH4DEVS_REVALIDATE`. To refresh on demand, mount the revalidate handler. It only acts on requests that carry your secret: ```ts // app/api/revalidate/route.ts import { createRevalidateHandler } from "dash4devs"; export const POST = createRevalidateHandler({ secret: process.env.REVALIDATE_SECRET }); ``` ## Entry points | Import | Contents | | --- | --- | | `dash4devs` | `DashClient`, `DashApiError`, `createRevalidateHandler`, every module, all types | | `dash4devs/react` | `DashProvider`, `useDash`, `CartProvider`, `useCart`, `AuthProvider`, `useAuth`, `DashImage`, `SignaturePad`, `useDashForm`, `MaintenancePage` | | `dash4devs/utils` | price, loyalty and bulk-discount helpers, `cdnImageUrl` | | `dash4devs/security` | `assertServerOnlyKey`, `maskKey`, `consoleWarningScript` | | `dash4devs/processors` | client-side payment helpers (Authorize.net Accept.js, Google Pay) | --- # Modules > Every area of the API, as a property of the client. Source: https://docs.dashfordevs.com/v1/sdk/modules · Topic: The dash4devs SDK · Updated: 2026-11-07 Each module maps to an area of the storefront API. | Module | Use it for | | --- | --- | | `dash.products` | `list(...)`, `getCore(slug)` for fast server rendering, full product data, search. | | `dash.categories`, `dash.brands` | Catalog structure for menus and filters. | | `dash.cart`, `dash.checkout` | Cart lines, totals, tax and shipping quotes, placing the order. | | `dash.payment`, `dash.paymentMethods` | Tokenized payments and saved methods. | | `dash.auth` | Shopper sign-up, sign-in and account. | | `dash.blog`, `dash.pages`, `dash.seo` | Posts, storefront pages and SEO data. | | `dash.pageGroup(slug)` | One page group: `.all()`, `.get(slug)`. | | `dash.media`, `dash.legal`, `dash.htmlTemplates` | Media, legal documents, HTML fragments. | | `dash.contact`, `dash.forms`, `dash.survey` | Contact forms, forms and surveys. | | `dash.calendar` | Availability and bookings. | | `dash.loyalty`, `dash.earnPoints`, `dash.discountStore`, `dash.referrals`, `dash.affiliates` | Loyalty and referral programs. | | `dash.subscriptions` | Customer subscriptions. | | `dash.shipping`, `dash.tax` | Shipping options and tax. | | `dash.coa` | Lab reports (certificates of analysis) for products. | | `dash.insights`, `dash.tracking` | Privacy-first analytics. | | `dash.sitemap` | Everything your sitemap needs. | | `dash.admin` | Secret-key only: write blog posts, page groups, products, media. | ```ts const { global } = await dash.getGlobalData(); // site settings, menus, in one call const { products } = await dash.products.list({ limit: 12 }); const { items } = await dash.pageGroup("services").all(); ``` ## Privacy defaults - Payment calls send line items as `Item 1`, `Item 2`: processors never see product names. - `dash.insights` sends opaque visitor and session ids, paths without query strings, referrer domains and device class. Never a customer id or page title. - Third-party pixels you call yourself (Meta, TikTok, Klaviyo) receive what you pass them. On a health catalog, only send product names to vendors covered by a BAA. --- # Images with DashImage > The right size for every screen, from the CDN. Source: https://docs.dashfordevs.com/v1/sdk/images · Topic: The dash4devs SDK · Updated: 2026-11-07 ```tsx import { DashImage } from "dash4devs/react"; ``` `DashImage` builds a `srcset` from the image's size ladder on **cdn.dashfordevs.com**, so a phone downloads a small file and a large screen never gets less than the uploaded resolution. - Paths under the CDN's folders (`/content/`, `/inventory/`, `/media/`, `/sites/`) are resolved against the CDN. - Your site's own files (`/images/logo.svg`) stay on your site. - `cdnImageUrl(path)` from `dash4devs/utils` does the same for your own markup. If you also use `next/image`, allow the CDN host: ```js // next.config.js images: { remotePatterns: [{ protocol: "https", hostname: "cdn.dashfordevs.com" }] } ``` --- # The dash4devs CLI > Scaffold a store, build it, ship its assets to the CDN. Source: https://docs.dashfordevs.com/v1/sdk/cli · Topic: The dash4devs SDK · Updated: 2026-11-07 | Command | What it does | | --- | --- | | `npx dash4devs init` | Scaffold a new storefront wired to your organization. | | `npx dash4devs build` | `next build`, then upload `.next/static` and `public/` to the CDN and switch the site to them atomically. | | `npx dash4devs deploy` | Build and deploy. | | `npx dash4devs status` | Which build is live, and whether the CDN has every asset. | `build` needs a secret key in the environment. Without one, or if the upload fails, it finishes as a plain `next build` with the site serving its own assets, so a CDN problem never takes your site down. Deploying to [DashForDevs Cloud](https://docs.dashfordevs.com/v1/cloud)? Cloud builds from your GitHub repository on every push; you do not need to run the CLI yourself. --- # What Cloud is > Hosting in the same account as your business, on AWS, without managing AWS. Source: https://docs.dashfordevs.com/v1/cloud/overview · Topic: DashForDevs Cloud · Updated: 2026-11-07 **cloud.dashfordevs.com** hosts the things your business runs on: your website, your backend, its database. It runs on AWS underneath; you never touch AWS. ## Available today | Product | What you get | | --- | --- | | **App Platform** | Connect a GitHub repository; every push to your branch builds and deploys. Environment variables, custom domains, logs, deploy history, rollback. | | **Databases** | Managed PostgreSQL. Connection details on request, trusted sources only. | | **Projects** | Group an app and its database, see their activity together. | | **API tokens** | Script your Cloud. | Spaces (object storage with CDN), Droplets (virtual machines), firewalls, monitoring and alerts are planned and appear in the console as they go live. ## Who can use it Cloud belongs to an organization. Two permissions control it: - `cloud:access`: open the organization's Cloud and see its resources; - `cloud:manage`: create, change and delete resources, tokens and billing. Owners and Admins have both. Give a contractor `cloud:access` to look without touching. ## Shared or dedicated You choose per resource. | | Shared | Dedicated | | --- | --- | --- | | Apps | Run on discounted spare capacity, small sizes | Their own capacity, security group and encryption key; autoscaling | | Databases | A database and user on a shared tenant server (never the platform's own) | Their own database server and encryption key | | Price | Lower | Higher | | HIPAA | **Never** | Eligible: AWS services under our BAA | Every HIPAA-eligible option carries a **HIPAA** badge in the console. ## Addresses Apps get an address on Cloud's own apps domain (`-.`), never under dashfordevs.com, so your app can never share cookies with ours. Add your own domain with a CNAME and we issue the certificate. --- # Deploy an app from GitHub > Connect a repository once. Every push after that deploys itself. Source: https://docs.dashfordevs.com/v1/cloud/deploy-an-app · Topic: DashForDevs Cloud · Updated: 2026-11-07 1. **Connect GitHub.** In **cloud.dashfordevs.com → App Platform**, choose **Connect to GitHub** and install the DashForDevs \[GitHub App\](https://docs.github.com/en/apps/using-github-apps/installing-a-github-app-from-a-third-party) on your account or organization. Pick which repositories it may see. 2. **Create an app.** Choose the repository, the branch and, for a monorepo, the source folder. 3. **Check the build settings.** If the repository has a `Dockerfile`, we use it. Otherwise we detect the language from its files (`package.json`, `requirements.txt`, `pyproject.toml`, `go.mod` and others) and suggest build and run commands you can edit. Set the port your app listens on and a health check path. 4. **Add environment variables** (below), and attach a Cloud database if it needs one. 5. **Create.** We fetch the commit, build an image, and start it behind our load balancer. Follow the build and deploy logs live. ```dfd-diagram { "type": "flow", "title": "From git push to live", "layout": "LR", "nodes": [ { "id": "push", "label": "git push", "kind": "person" }, { "id": "gh", "label": "GitHub webhook", "kind": "event", "detail": "Signed by GitHub; we verify it before doing anything." }, { "id": "build", "label": "Build", "kind": "service", "detail": "Your commit is fetched and built into an image in an isolated build job." }, { "id": "registry", "label": "Image", "kind": "data", "detail": "Stored in your app's private image repository." }, { "id": "release", "label": "Release", "kind": "service", "detail": "A new version starts. Traffic moves only when its health check passes." }, { "id": "live", "label": "Live", "kind": "state", "detail": "Previous version kept for rollback." } ], "edges": [ { "from": "push", "to": "gh" }, { "from": "gh", "to": "build" }, { "from": "build", "to": "registry" }, { "from": "registry", "to": "release" }, { "from": "release", "to": "live" } ], "steps": [ { "title": "You push", "focus": ["push", "gh"], "text": "A push to the app's branch notifies Cloud." }, { "title": "We build", "focus": ["build", "registry"], "text": "Dockerfile if present, otherwise detected from your files." }, { "title": "We release safely", "focus": ["release", "live"], "text": "The new version must pass its health check before it gets traffic. If it fails, the old one keeps serving." } ] } ``` ## Environment variables Set them per app. Secret values are encrypted, masked in the console, and injected when the app starts; they never appear in build logs. A change triggers a redeploy, and the change is recorded with the variable **names** that changed, never the values. ## Custom domains Add your domain to the app, then create the CNAME record we show you at your DNS provider. We issue and renew the TLS certificate. ## Logs, history and rollback Each app has build, deploy and runtime logs, the list of deployments with their commits, and its activity history. Roll back by redeploying an earlier deployment. --- # Managed databases > PostgreSQL you do not have to run. Source: https://docs.dashfordevs.com/v1/cloud/databases · Topic: DashForDevs Cloud · Updated: 2026-11-07 1. In **cloud.dashfordevs.com → Databases**, choose **Create database**. 2. Name it, pick PostgreSQL's version, choose **shared** or **dedicated**, and optionally a project. 3. Wait for **Ready** (a shared database takes seconds). 4. Attach it to an app, or open **Connection details** to see the host, port, database, user and password. ## How credentials are kept - The password is **not stored in our database**. It lives in a secrets vault and is read only when a member with permission asks for the connection details; that request is recorded. - Databases accept connections only from trusted sources: the apps you attach them to, inside the private network. ## Shared and dedicated A **shared** database is your own database and login on a tenant Postgres server shared with other customers' databases (never with the DashForDevs platform's own data). It is the cheapest way to start, and it is **never HIPAA**. A **dedicated** database is its own server with its own encryption key, and is HIPAA eligible. --- # Cloud billing > Pay as you go, per resource-hour, capped at a monthly price. Source: https://docs.dashfordevs.com/v1/cloud/billing · Topic: DashForDevs Cloud · Updated: 2026-11-07 - **Pay as you go.** Each resource is billed by the hour it exists. - **Capped.** A resource's hourly charges stop at its monthly price (672 hours), so a full month never costs more than the listed monthly price. - **Separate from your platform plan.** Cloud has its own billing account per organization, its own card and its own monthly invoice. - **Current prices** for every size are shown in the console when you create a resource. Members with `cloud:manage` manage Cloud billing; see usage so far this month on the Billing page of the console. --- # The security model > Defense in depth, written down, so you can check our work. Source: https://docs.dashfordevs.com/v1/security/security-model · Topic: Security model · Updated: 2026-11-07 ## 1\. Isolation - **One database per service**, each with its own credentials. A service can only read its own tables. - **Tenant isolation in every query.** Every row carries its organization id, and the organization always comes from a verified identity (the signed papi identity or the verified session), never from a header, path or body the caller controls. Every service runs a contract test: another organization's records answer `404`, and writes to them are refused. - **The customer PHI vault** is on its own database server, on no gateway, reachable only from the customers service's tasks over the private network. It re-checks the person's membership itself, so a compromised caller still cannot read an organization the person is not in. - **Private networking.** Databases have no public address. Services talk through tapi, a private load balancer. ## 2\. Encryption - **In transit:** TLS everywhere, HSTS on our hosts, no plain HTTP. - **At rest:** every database and bucket is encrypted. Sensitive services (orders, the vault, payments, papi) use their own keys. - **Field-level:** personal fields (names, emails, phones, addresses, notes), processor and carrier credentials, TOTP seeds and stored API secrets are encrypted per field under the owning service's key. Lookups use keyed one-way **blind indexes**, so an email can be searched without being stored in clear. ## 3\. Identity - OpenID Connect with PKCE; confidential clients only for web apps, so **no token reaches browser JavaScript**. - 10-minute access tokens; rotating 30-day refresh tokens with reuse detection. - Session cookies scoped to a single host, `Secure`, `HttpOnly`, `SameSite=Lax`. - Two-factor available to everyone, required for staff; sensitive actions check the session's `amr`, not just the account's setting. - Password policy per [NIST SP 800-63B](https://pages.nist.gov/800-63-4/sp800-63b.html): length and breached-password checks, no forced rotation. Rate limits per IP and per email; constant-time responses on sign-in and reset. ## 4\. Service-to-service trust - papi signs a 60-second **Ed25519 identity** for each request (organization, key id, key class, scope, route). Services verify it with papi's public key and never see a tenant key. - Services call each other and deliver events with **HMAC-signed requests** using pairwise keys, with nonces to block replays. - **Two checks:** the gateway authorizes, and the service authorizes again. ## 5\. Payments and PHI - **No card data**: cards become one-time tokens in the shopper's browser ([PCI DSS SAQ A](https://www.pcisecuritystandards.org/)). We store processor ids, brand, last four and expiry only. - **Processors never see products**: every request to a processor passes one redaction function; lines become `Item 1`, descriptions become the order number. - **Analytics stores no personal data**: yearly-rotating pseudonyms with no reverse map, coarsened values, totals only, small counts suppressed. - Features that would send PHI to a vendor without a BAA are built but off for HIPAA organizations. ## 6\. Logging without leaking - Every service keeps an **append-only, hash-chained audit log**, verified for breaks. - **Traces** record route templates, statuses, actors, organizations and touched record ids and field names. Never values. - Request logs at the public edge record the route template only: no path values, no query strings, no IP addresses, no keys. - Secrets never appear in logs, spans or error bodies. ## 7\. The edge - A **web application firewall** in front of the public API: IP reputation lists, known-bad inputs, core rules, per-IP and per-key rate ceilings. - Security headers on every response: `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, strict `Referrer-Policy`, `Cross-Origin-Opener-Policy`. - Outgoing webhooks refuse private, loopback and link-local destinations (SSRF) and never follow redirects. - Uploaded files are identified by their bytes; anything executable or disguised is refused, and SVG scripts are stripped. - Rich text is cleaned by an allowlist on the way in: nothing that runs is ever stored. ## 8\. Engineering practice - Every service's tests run on Postgres, against throwaway databases, never against live data. - Infrastructure is declared in Terraform; changes are reviewed and applied deliberately. - Changes that can fail requests ship with a rollback plan. ## Report a vulnerability Email **[support@dashfordevs.com](mailto:support@dashfordevs.com)** with the subject "Security", the details and how to reproduce. Please give us reasonable time to fix before disclosing, and do not access data that is not yours. We answer every report. --- # Read this first (agents) > Facts and rules for an AI model working with DashForDevs. Source: https://docs.dashfordevs.com/v1/ai-agents/read-this-first · Topic: For AI agents · Updated: 2026-11-07 This page is written for AI models and the people who configure them. It is short on purpose. ## Facts - **Public API base:** `https://papi.dashfordevs.com`. Storefront reads are under `/api/storefront/...`; admin writes under `/api/storefront/admin/...` (secret key only). - **Auth:** header `X-API-Key: `. `pk_` keys are read-only; `sk_` keys can write. One key belongs to one organization and can never reach another. - **MCP server:** `https://papi.dashfordevs.com/mcp/org` with `Authorization: Bearer sk_...`. See [Connect an assistant](https://docs.dashfordevs.com/v1/ai-agents/connect-an-assistant). - **Errors:** JSON `{"error": "", "message": "..."}`. Branch on `error`. `501 not_implemented` and `503 not_available_yet` (with `"available": false`) are final: do not retry. - **Rate limits:** 120 requests per minute for a public key, 300 for a secret key. On `429`, wait `retry_after` seconds. - **Docs as text:** every page here has a Markdown copy at the same address plus `.md`. [/llms.txt](https://docs.dashfordevs.com/llms.txt) indexes all pages; [/llms-full.txt](https://docs.dashfordevs.com/llms-full.txt) is everything in one file. ## Rules 1. **Never put a secret key where a browser, a user or a log can read it.** Not in client code, not in a `NEXT_PUBLIC_` variable, not in a reply to the user, not in a commit. 2. **Never write personal data into public content.** Blog posts, page groups and media are public. Customer names, emails, health details and order contents must never go there. 3. **Draft first.** Create blog posts and page items as drafts unless the person explicitly asked you to publish. 4. **Ask before deleting.** Deletions through the admin API are real and immediate. 5. **Do not retry writes blindly.** A write that timed out may have succeeded; read before writing again. 6. **Stay inside the organization you were given.** Do not ask for, guess or use other organizations' keys. 7. **Respect the gates.** If a route answers `not_available_yet` because of the personal-data gate, tell the person; do not look for a way around it. 8. **No em dashes in content you write for a DashForDevs organization's site**, unless the person asks for them. --- # Connect an assistant (MCP) > Let Claude write posts, update pages and read forms for one organization. Source: https://docs.dashfordevs.com/v1/ai-agents/connect-an-assistant · Topic: For AI agents · Updated: 2026-11-07 The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server lets an AI assistant work with **one organization** using that organization's **secret key**. Every tool call goes through the same checks as the API: key, organization status, secret-only admin routes, rate limits and personal-data gates. The key cannot reach any other organization. ## 1\. Make a key for the assistant In the dashboard, open **Developers → API keys** and create a key just for the assistant, named for it ("Claude, John's laptop"). Then you can see its traffic separately and revoke it alone. ## 2\. Connect **[Claude Code](https://docs.anthropic.com/en/docs/claude-code/mcp):** ```bash claude mcp add --transport http dashfordevs https://papi.dashfordevs.com/mcp/org \ --header "Authorization: Bearer sk_live_YOUR_SECRET_KEY" ``` **Clients that only accept a URL** (some connectors): ``` https://papi.dashfordevs.com/mcp/org/sk_live_YOUR_SECRET_KEY ``` A key in a URL can end up in logs. Prefer the header form; if you must use the URL, use a dedicated key and regenerate it if it is ever shared. The dashboard's **MCP** page (sidebar: **MCP → MCP server**) shows these snippets and the live tool list. ## 3\. Tools | Tool | Does | | --- | --- | | `org_info` | The organization the key belongs to. | | `blog_list`, `blog_get` | Read posts (list without bodies; one with its HTML). | | `blog_create`, `blog_update`, `blog_delete` | Write posts. Bodies are HTML; code as `
`. |
| `blog_categories`, `blog_category_create` | Categories for posts. |
| `page_groups_list`, `page_group_create` | Page groups (content types). Create is idempotent by slug. |
| `page_items_list`, `page_item_get` | Read items in a page group. |
| `page_item_create`, `page_item_update`, `page_item_delete` | Write items, with their custom fields. |
| `contact_forms_list`, `contact_form_get` | Read contact form submissions (personal data: held back by the personal-data gate where it applies). |
| `admin_endpoints` | List every admin API route the key can call. |
| `admin_request` | Call one of those routes directly, for anything not covered above. |

## Good prompts

-   "Draft a blog post about our spring menu in the Seasonal category. Keep it as a draft."
-   "List the items in the Services page group and fix any description over 160 characters."
-   "Summarize this week's contact form submissions by topic, without quoting names or emails."


---

# Build a DashForDevs site with an AI coding agent

> Give the agent the right context and the right guardrails.

Source: https://docs.dashfordevs.com/v1/ai-agents/build-with-ai · Topic: For AI agents · Updated: 2026-11-07

AI coding agents are very good at storefront work when they have accurate context. Give them these docs instead of letting them guess.

## Give it context

-   Point it at **[https://docs.dashfordevs.com/llms-full.txt](https://docs.dashfordevs.com/llms-full.txt)**, or the specific pages it needs with `.md` added (for example `/v1/sdk/modules.md`).
-   Tell it the stack: Next.js App Router, the `dash4devs` SDK, server components for data.

## Give it guardrails

Paste this into your agent's instructions:

```text
- Use the dash4devs SDK. Server code uses lib/dash.server.ts (secret key, import "server-only").
  Browser code uses lib/dash.client.ts (public key). Never import the server client from a client component.
- Never put an sk_ key in a NEXT_PUBLIC_ variable, client code, a log line or a commit.
- Handle DashApiError: 404 -> notFound(); 501/503 with available:false -> show a friendly "not available" state, never retry.
- Use DashImage for every image from the API.
- No em dashes in copy.
```

## Check its work

-   `grep -r "sk_" app components lib` finds nothing outside your server-only module and `.env*`.
-   The production build passes and pages render on the server (view source shows the content).
-   Live keys and your domain under **Allowed origins** before launch.


---

# Overview

> Every public route, by area, straight from the gateway.

Source: https://docs.dashfordevs.com/v1/api-reference/overview · Topic: Storefront API reference · Updated: 2026-11-07

This reference is **generated from papi's route table**, the same table the gateway uses to route every request. If a route ships, it appears here; if it is removed, it disappears.

-   Base URL: `https://papi.dashfordevs.com`
-   Auth: `X-API-Key` header ([keys](https://docs.dashfordevs.com/v1/authentication))
-   Errors: [error codes](https://docs.dashfordevs.com/v1/error-handling)
-   Typed access: the [SDK](https://docs.dashfordevs.com/v1/sdk)

| Area | Routes | Available |
| --- | --- | --- |
| [Catalog](https://docs.dashfordevs.com/v1/api-reference/catalog) | 22 | 22 |
| [Cart, checkout and orders](https://docs.dashfordevs.com/v1/api-reference/cart-checkout-orders) | 48 | 48 |
| [Customers and calendar](https://docs.dashfordevs.com/v1/api-reference/customers-and-calendar) | 32 | 32 |
| [Content](https://docs.dashfordevs.com/v1/api-reference/content) | 25 | 25 |
| [Forms and email](https://docs.dashfordevs.com/v1/api-reference/forms-and-email) | 5 | 5 |
| [Shipping](https://docs.dashfordevs.com/v1/api-reference/shipping) | 7 | 7 |
| [Payments](https://docs.dashfordevs.com/v1/api-reference/payments) | 14 | 14 |
| [Analytics](https://docs.dashfordevs.com/v1/api-reference/analytics) | 5 | 5 |
| [Automations](https://docs.dashfordevs.com/v1/api-reference/automations) | 3 | 2 |
| [Billing](https://docs.dashfordevs.com/v1/api-reference/billing) | 2 | 2 |
| [Platforms](https://docs.dashfordevs.com/v1/api-reference/platforms) | 1 | 0 |
| [Gateway](https://docs.dashfordevs.com/v1/api-reference/gateway) | 3 | 3 |
| [Admin API (secret key)](https://docs.dashfordevs.com/v1/api-reference/admin-api) | 48 | 42 |

215 routes in total.


---

# Catalog

> Products, categories, brands, reviews, lab reports and stock.

Source: https://docs.dashfordevs.com/v1/api-reference/catalog · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/auth/restock-requests` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/restock-requests/{request_id}/unsubscribe` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/featured-variations` | Public or secret key | Live |  |
| `GET` | `/api/storefront/products` | Public or secret key | Live |  |
| `GET` | `/api/storefront/customer/custom-prices` | Public or secret key | Live (customer data)\* | needs the signed-in customer |
| `GET` | `/api/storefront/reviews` | Public or secret key | Live (customer data)\* | shopper names; reviews held back until the BAA |
| `GET` | `/api/storefront/products/{slug}/core` | Public or secret key | Live |  |
| `GET` | `/api/storefront/products/{slug}/options` | Public or secret key | Live |  |
| `GET, POST` | `/api/storefront/products/{slug}/reviews` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/products/{slug}/notify-restock` | Public or secret key | Live (customer data)\* |  |
| `GET, POST` | `/api/restock/unsubscribe/{token}` | No key | Live (customer data)\* |  |
| `GET` | `/api/storefront/products/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/seo/product/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/categories` | Public or secret key | Live |  |
| `GET` | `/api/storefront/categories/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/brands` | Public or secret key | Live |  |
| `GET` | `/api/storefront/brands/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/cart-tier-discounts` | Public or secret key | Live |  |
| `GET` | `/api/storefront/promotions` | Public or secret key | Live |  |
| `GET` | `/api/storefront/coa` | Public or secret key | Live |  |
| `GET` | `/api/storefront/coa/{product_slug}/{variation_slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/coa/{product_slug}` | Public or secret key | Live |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Cart, checkout and orders

> Carts, checkout, orders, discounts, loyalty, referrals, affiliates and tax.

Source: https://docs.dashfordevs.com/v1/api-reference/cart-checkout-orders · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/storefront/auth/merge-cart` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/auth/orders` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/auth/orders/{order_id}` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/auth/discounts` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/add` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/locations` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/cart/user` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/cart/{cart_id}` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/update` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/change` | Public or secret key | Live (customer data)\* |  |
| `DELETE` | `/api/storefront/cart/{cart_id}/remove/{size_id}` | Public or secret key | Live (customer data)\* |  |
| `DELETE` | `/api/storefront/cart/{cart_id}/clear` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/contact` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/migrate` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/discount/validate` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/apply-discount` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/remove-discount` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/cart/{cart_id}/upsells` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/upsells/start` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/cart/{cart_id}/upsells/status` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/cart/{cart_id}/upsells/add` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/checkout/start` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/checkout/progress` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/checkout/complete` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/wholesale/place-request` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/checkout/resume` | Public or secret key | Live (customer data)\* |  |
| `GET, POST` | `/api/storefront/checkout/repayment` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/checkout/repayment/cancel` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/tax/calculate` | Public or secret key | Live | state, zip and size ids only - no person |
| `POST` | `/api/webhooks/klaviyo/generate-discount` | No key | Live (customer data)\* |  |
| `POST` | `/api/storefront/referrals/validate` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/referrals/dashboard` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/affiliates/form-config` | Public or secret key | Live |  |
| `POST` | `/api/storefront/affiliates/apply` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/affiliates/status` | Public or secret key | Live (customer data)\* |  |
| `GET, PUT` | `/api/storefront/affiliates/dashboard` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/affiliates/create-code` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/affiliates/deactivate-code` | Public or secret key | Live (customer data)\* |  |
| `GET, POST` | `/api/storefront/affiliates/product-requests` | Public or secret key | Live (customer data)\* |  |
| `GET, PUT` | `/api/storefront/affiliates/product-requests/{request_id}` | Public or secret key | Live (customer data)\* |  |
| `GET, POST` | `/api/customer/subscriptions/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/cancel/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/pause/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/resume/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/update-payment/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/change-plan/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/skip/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/subscriptions/{sub_id}/resubscribe/` | Public or secret key | Live (payments)\* |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Customers and calendar

> Shopper accounts, profiles, list subscriptions, calendar availability and booking.

Source: https://docs.dashfordevs.com/v1/api-reference/customers-and-calendar · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/storefront/auth/check-exists` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/request-otp` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/verify-otp` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/login` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/set-password` | No key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/forgot-password` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/reset-password` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/refresh` | No key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/logout` | No key | Live (customer data)\* |  |
| `GET, PATCH` | `/api/storefront/auth/me` | No key | Live (customer data)\* |  |
| `GET` | `/api/storefront/auth/sessions` | No key | Live (customer data)\* |  |
| `POST` | `/api/storefront/auth/sessions/{session_id}/revoke` | No key | Live (customer data)\* |  |
| `ANY` | `/api/storefront/auth/profile` | No key | Live (customer data)\* | production has no method decorator here |
| `GET, POST` | `/api/storefront/auth/addresses` | No key | Live (customer data)\* |  |
| `PUT, PATCH, DELETE` | `/api/storefront/auth/addresses/{address_id}` | No key | Live (customer data)\* |  |
| `GET` | `/api/storefront/auth/check-ban` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/calendar/settings` | Public or secret key | Live |  |
| `GET` | `/api/storefront/calendar/days-off` | Public or secret key | Live |  |
| `GET` | `/api/storefront/calendar/availability` | Public or secret key | Live (customer data)\* | derived from booked appointments |
| `POST` | `/api/storefront/calendar/book` | Public or secret key | Live (customer data)\* |  |
| `GET, POST` | `/api/storefront/calendar/reschedule/{token}` | No key | Live (customer data)\* |  |
| `GET` | `/api/storefront/customers/rank` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/customers/subscribe` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/upload` | Public or secret key | Live (customer data)\* | shopper files (e.g. ID); not built |
| `GET` | `/api/storefront/loyalty/summary` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/discount-store` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/discount-store/redeem` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/earn-points/tasks` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/earn-points/complete` | Public or secret key | Live (customer data)\* |  |
| `GET` | `/api/storefront/surveys/{slug}` | Public or secret key | Live | the survey's questions; no answers |
| `POST` | `/api/storefront/surveys/{slug}/submit` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/landing-survey/submit` | Public or secret key | Live (customer data)\* |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Content

> Pages, global data, blog, page groups, media, legal documents and docs.

Source: https://docs.dashfordevs.com/v1/api-reference/content · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/html-templates/{tmpl_id_or_slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/pages` | Public or secret key | Live |  |
| `GET, POST` | `/api/storefront/page` | Public or secret key | Live |  |
| `GET` | `/api/storefront/smart-media/{slug_or_id}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/media/resolve` | Public or secret key | Live |  |
| `GET` | `/api/storefront/media/by-name/{name}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/media/folder/{folder_name}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/content-types` | Public or secret key | Live |  |
| `GET` | `/api/storefront/content-types/{type_slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/content-types/{type_slug}/{item_slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/blog/posts` | Public or secret key | Live |  |
| `GET` | `/api/storefront/blog/posts/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/seo/blog/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/blog/categories` | Public or secret key | Live |  |
| `GET` | `/api/storefront/legal` | Public or secret key | Live |  |
| `GET` | `/api/storefront/legal/{slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/docs` | Public or secret key | Live |  |
| `GET` | `/api/storefront/docs/search` | Public or secret key | Live |  |
| `GET` | `/api/storefront/docs/{topic_slug}` | Public or secret key | Live |  |
| `GET` | `/api/storefront/docs/{topic_slug}/{section_slug}` | Public or secret key | Live |  |
| `GET` | `/api/v1/cdn/status` | Secret key only | Live |  |
| `POST` | `/api/v1/cdn/manifest` | Secret key only | Live |  |
| `POST` | `/api/v1/cdn/uploaded` | Secret key only | Live |  |
| `POST` | `/api/v1/cdn/activate` | Secret key only | Live |  |
| `POST` | `/api/v1/cdn/purge` | Secret key only | Live |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Forms and email

> Contact forms, newsletters and email.

Source: https://docs.dashfordevs.com/v1/api-reference/forms-and-email · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/email/config` | Public or secret key | Live |  |
| `POST` | `/api/storefront/email/identify` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/email/track` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/contact` | Public or secret key | Live (customer data)\* |  |
| `POST` | `/api/storefront/contact-form` | Public or secret key | Live (customer data)\* |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Shipping

> Shipping rates and options.

Source: https://docs.dashfordevs.com/v1/api-reference/shipping · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/shipping-locations` | Public or secret key | Live |  |
| `GET` | `/api/storefront/banned-state-names` | Public or secret key | Live |  |
| `POST` | `/api/storefront/shipping/rates` | Public or secret key | Live | destination + package weights to carriers; nothing stored, no product names |
| `POST` | `/api/storefront/shipping/validate-address` | Public or secret key | Live | nothing stored |
| `GET` | `/api/storefront/shipping/track/{tracking_number}` | Public or secret key | Live (customer data)\* |  |
| `ANY` | `/api/shipstation/xml` | No key | Live (customer data)\* | order feed |
| `POST` | `/api/ups/webhook` | No key | Live (customer data)\* |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Payments

> Storefront payments and processor webhooks.

Source: https://docs.dashfordevs.com/v1/api-reference/payments · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/auth/invoices` | Public or secret key | Live (payments)\* |  |
| `GET` | `/api/storefront/auth/stripe-payment-methods` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/auth/billing-portal` | Public or secret key | Live (payments)\* |  |
| `GET` | `/api/storefront/payment/processor` | Public or secret key | Live (payments)\* |  |
| `GET` | `/api/storefront/payment/client-config` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/payment/charge` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/payment/authorize` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/payment/capture` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/storefront/payment/void` | Public or secret key | Live (payments)\* |  |
| `GET, POST` | `/api/customer/payment-methods/` | Public or secret key | Live (payments)\* |  |
| `DELETE` | `/api/customer/payment-methods/{profile_id}/delete/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/customer/payment-methods/{profile_id}/set-default/` | Public or secret key | Live (payments)\* |  |
| `POST` | `/api/webhooks/authorize/recurring/` | No key | Live (payments)\* |  |
| `POST` | `/api/webhooks/quickbooks/` | No key | Live (payments)\* |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Analytics

> Privacy-first tracking and insights.

Source: https://docs.dashfordevs.com/v1/api-reference/analytics · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/analytics/config` | Public or secret key | Live |  |
| `POST` | `/api/storefront/analytics/track-visit` | Public or secret key | Live |  |
| `POST` | `/api/storefront/analytics/tracking/actions/create` | Public or secret key | Live |  |
| `POST` | `/api/storefront/analytics/tracking/events` | Public or secret key | Live |  |
| `POST` | `/api/storefront/analytics/collect` | Public key (beacon) | Live | key in X-API-Key or ?k= / ?api\_key= (sendBeacon cannot set headers); no origin check; an unknown key is a silent 204, as production |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Automations

> Inbound webhooks for flows.

Source: https://docs.dashfordevs.com/v1/api-reference/automations · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/webhooks/{org_id}/trigger/{trigger_type}` | No key | Not built yet |  |
| `GET, POST` | `/api/webhooks/{org_id}/workflow/{workflow_id}/{webhook_secret}` | No key | Live | automations stores ids and field names only (DFD25 §0.1) |
| `GET, POST` | `/automations/v1/hooks/{org_id}/workflow/{workflow_id}/{webhook_secret}/` | No key | Live |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Billing

> Platform billing webhooks.

Source: https://docs.dashfordevs.com/v1/api-reference/billing · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/stripe/platform-webhook` | No key | Live |  |
| `POST` | `/billing/v1/stripe/webhook/` | No key | Live |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Platforms

> White-label platform routes.

Source: https://docs.dashfordevs.com/v1/api-reference/platforms · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/platform/analytics/collect` | No key | Not built yet |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Gateway

> Routes papi answers itself.

Source: https://docs.dashfordevs.com/v1/api-reference/gateway · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `GET` | `/api/storefront/ping` | Public or secret key | Live |  |
| `GET` | `/api/storefront/global` | Public or secret key | Live | papi org profile + inventory and content parts |
| `GET` | `/api/storefront/sitemap` | Public or secret key | Live | inventory + content parts |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.


---

# Admin API (secret key)

> Write to your organization from your server, with the secret key.

Source: https://docs.dashfordevs.com/v1/api-reference/admin-api · Topic: Storefront API reference · Updated: 2026-11-07

Generated from the gateway's route table. Base URL `https://papi.dashfordevs.com`. Send your key in `X-API-Key`.

Every route here needs the **secret key**. Call them from your server only.

| Method | Path | Key | Status | Notes |
| --- | --- | --- | --- | --- |
| `POST` | `/api/storefront/admin/products/import` | Secret key only | Live |  |
| `GET, PUT, DELETE` | `/api/storefront/admin/products/{product_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/products` | Secret key only | Live |  |
| `POST` | `/api/storefront/admin/categories/import` | Secret key only | Live |  |
| `GET, PUT, DELETE` | `/api/storefront/admin/categories/{category_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/categories` | Secret key only | Live |  |
| `GET, POST, DELETE` | `/api/storefront/admin/page-groups/{type_slug}/items/{item_slug}/images` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/page-groups/{type_slug}/items/{item_slug}/gallery` | Secret key only | Live |  |
| `GET, PATCH, DELETE` | `/api/storefront/admin/page-groups/{type_slug}/items/{item_slug}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/page-groups/{type_slug}/items` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/page-groups` | Secret key only | Live |  |
| `PUT, DELETE` | `/api/storefront/admin/brands/{brand_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/brands` | Secret key only | Live |  |
| `GET, PUT, DELETE` | `/api/storefront/admin/blog/posts/{post_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/blog/posts` | Secret key only | Live |  |
| `PUT, DELETE` | `/api/storefront/admin/reviews/{review_id}` | Secret key only | Live (customer data)\* |  |
| `GET` | `/api/storefront/admin/reviews` | Secret key only | Live (customer data)\* |  |
| `POST` | `/api/storefront/admin/contact-forms/{form_id}/reply` | Secret key only | Live (customer data)\* |  |
| `GET, PUT, DELETE` | `/api/storefront/admin/contact-forms/{form_id}` | Secret key only | Live (customer data)\* |  |
| `GET` | `/api/storefront/admin/contact-forms` | Secret key only | Live (customer data)\* |  |
| `GET, PUT` | `/api/storefront/admin/orders/{order_id}` | Secret key only | Live (customer data)\* |  |
| `GET` | `/api/storefront/admin/orders` | Secret key only | Live (customer data)\* |  |
| `POST` | `/api/storefront/admin/customers/import` | Secret key only | Live (customer data)\* |  |
| `GET, PUT` | `/api/storefront/admin/customers/{customer_id}` | Secret key only | Live (customer data)\* |  |
| `GET` | `/api/storefront/admin/customers` | Secret key only | Live (customer data)\* |  |
| `POST` | `/api/storefront/admin/media/generate-variants` | Secret key only | Live |  |
| `GET` | `/api/storefront/admin/media/variant-status` | Secret key only | Live |  |
| `POST` | `/api/storefront/admin/media/video-prepare` | Secret key only | Not built yet |  |
| `POST` | `/api/storefront/admin/media/video-complete` | Secret key only | Not built yet |  |
| `DELETE` | `/api/storefront/admin/media/{media_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/media` | Secret key only | Live |  |
| `POST` | `/api/storefront/admin/emails/send` | Secret key only | Not built yet |  |
| `GET` | `/api/storefront/admin/emails/threads/{thread_id}` | Secret key only | Not built yet |  |
| `GET` | `/api/storefront/admin/emails/threads` | Secret key only | Not built yet |  |
| `PUT, DELETE` | `/api/storefront/admin/products/{product_id}/attributes/{attribute_id}/options/{option_id}` | Secret key only | Live |  |
| `POST` | `/api/storefront/admin/products/{product_id}/attributes/{attribute_id}/options` | Secret key only | Live |  |
| `PUT, DELETE` | `/api/storefront/admin/products/{product_id}/attributes/{attribute_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/products/{product_id}/attributes` | Secret key only | Live |  |
| `PUT, DELETE` | `/api/storefront/admin/products/{product_id}/sizes/{size_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/products/{product_id}/sizes` | Secret key only | Live |  |
| `DELETE` | `/api/storefront/admin/products/{product_id}/files/{file_id}` | Secret key only | Live |  |
| `GET` | `/api/storefront/admin/products/{product_id}/files` | Secret key only | Live |  |
| `POST` | `/api/storefront/admin/emails/threads/{thread_id}/reply` | Secret key only | Not built yet |  |
| `GET` | `/api/storefront/admin/contact-forms/{form_id}/replies` | Secret key only | Live (customer data)\* |  |
| `PUT, DELETE` | `/api/storefront/admin/blog/categories/{category_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/blog/categories` | Secret key only | Live |  |
| `PUT, DELETE` | `/api/storefront/admin/discount-codes/{code_id}` | Secret key only | Live |  |
| `GET, POST` | `/api/storefront/admin/discount-codes` | Secret key only | Live |  |

\* **Customer data and payments** routes work for every organization except HIPAA organizations, which get `503 not_available_yet` (with `"available": false`) until that organization's HIPAA path is complete. **Not built yet** routes answer `501 not_implemented`.