Docs

How COA Comply works

Set up your account, put your COAs on your website, and connect your own systems and AI agents. Can't find something? Email support@coacomply.com.

Companies & brands

Every account is a company with one or more brands. The company is who you are as a business: it holds your team, billing, API keys and AI instructions. A brand is what your customers see: the name on the package, its logo and colors, and the COA pages behind its QR codes.

Company

  • Team members and their roles
  • Plan, usage and billing
  • API keys and the MCP server
  • AI instructions and the activity log
  • An optional logo, shown only in the dashboard

Brand

  • Name, logo, favicon, colors and website for its public COA pages
  • Its COAs, QR codes and embeds
  • COA processing: auto-publish and product categories
  • Compliance documents, state rules and potency tolerance

How they relate

  • Every COA belongs to one brand. The AI matches each upload to a brand by its name and the other names you give it (for example "WYLD" and "Wyld Canna").
  • Consumers only ever see the brand. Your company name never appears on public pages, so a holding company can run several consumer brands.
  • Team members and API keys can have access to every brand or only some. Embeds show one brand or all of them.
  • Pricing is per active brand ($49 a month each). Archiving a brand hides it from the dashboard; its published COAs and QR codes keep working.

One brand or several

Most accounts start with one brand, and then the company and the brand are the same thing: the dashboard shows a single Settings page with tabs for the brand and the account. Add a second brand (account menu → Add a brand) and the dashboard gains a company view with a Brands page, a brand switcher, and separate Company settings.

Embeds

An embed is a searchable list of your published COAs that you place on your own website. Create one in the dashboard under Embeds: choose the brand, layout, colors and buttons, and copy its snippet. Changes you save show on your site within a minute, with no need to paste the snippet again.

The snippet has two parts:

  • The iframe shows the COAs. It works on its own anywhere iframes are allowed.
  • embed.js (optional, about 2 KB) resizes the iframe to fit its content and connects your own search box to it. Load it once per page; it finds embeds that appear later, so it works with single-page apps.

In the embed's settings, Allowed sites limits which domains can show it. Leave it empty while testing, then add your domain (example.com also covers www.example.com; *.example.com covers every subdomain).

Any website (HTML)

Paste the snippet from the dashboard where the COAs should appear:

HTML
<iframe src="https://embed.coacomply.com/emb_YOUR_EMBED_ID" aria-label="Certificates of Analysis"
  loading="lazy" style="width:100%;min-height:640px;border:0"
  data-coacomply-embed></iframe>
<script async src="https://embed.coacomply.com/embed.js"></script>

React

JSX needs a few changes from the HTML: a style object, and an explicit value for the data attribute.

CoaEmbed.jsx
import { useEffect } from 'react'

export function CoaEmbed() {
  // Load the helper once; it finds embeds added later on its own.
  useEffect(() => {
    if (document.querySelector('script[data-coacomply]')) return
    const script = document.createElement('script')
    script.src = 'https://embed.coacomply.com/embed.js'
    script.async = true
    script.dataset.coacomply = ''
    document.body.appendChild(script)
  }, [])

  return (
    <iframe
      src="https://embed.coacomply.com/emb_YOUR_EMBED_ID"
      aria-label="Certificates of Analysis"
      loading="lazy"
      style={{ width: '100%', minHeight: 640, border: 0 }}
      data-coacomply-embed=""
    />
  )
}

Vue

Vue templates take the iframe as it is. Add the script once, here when the component mounts (or in your index.html).

CoaEmbed.vue
<script setup>
import { onMounted } from 'vue'

// Load the helper once; it finds embeds added later on its own.
onMounted(() => {
  if (document.querySelector('script[data-coacomply]')) return
  const script = document.createElement('script')
  script.src = 'https://embed.coacomply.com/embed.js'
  script.async = true
  script.dataset.coacomply = ''
  document.body.appendChild(script)
})
</script>

<template>
  <iframe
    src="https://embed.coacomply.com/emb_YOUR_EMBED_ID"
    aria-label="Certificates of Analysis"
    loading="lazy"
    style="width: 100%; min-height: 640px; border: 0"
    data-coacomply-embed
  />
</template>

Next.js

Use next/script so the helper loads after the page and only once across client-side navigation.

app/coas/page.jsx
import Script from 'next/script'

export default function CoasPage() {
  return (
    <>
      <iframe
        src="https://embed.coacomply.com/emb_YOUR_EMBED_ID"
        aria-label="Certificates of Analysis"
        loading="lazy"
        style={{ width: '100%', minHeight: 640, border: 0 }}
        data-coacomply-embed=""
      />
      <Script src="https://embed.coacomply.com/embed.js" strategy="lazyOnload" />
    </>
  )
}

Nuxt

useHead adds the script once; the key stops duplicates when several pages use it.

pages/coas.vue
<script setup>
useHead({ script: [{ src: 'https://embed.coacomply.com/embed.js', async: true, key: 'coacomply' }] })
</script>

<template>
  <iframe
    src="https://embed.coacomply.com/emb_YOUR_EMBED_ID"
    aria-label="Certificates of Analysis"
    loading="lazy"
    style="width: 100%; min-height: 640px; border: 0"
    data-coacomply-embed
  />
</template>

WordPress

  1. Edit the page and add a Custom HTML block where the COAs should go.
  2. Paste the snippet and update the page.

On WordPress.com, scripts and iframes need a plan that allows custom code. If the script is removed, the iframe still works: it just keeps its 640px height and scrolls inside.

Squarespace

  1. Edit the page, add a block and choose Code.
  2. Set it to HTML, paste the snippet, and turn off Display source.
  3. Save. Code blocks don't run in the editor preview, so check the live page.

Code blocks with scripts need a Squarespace plan that supports custom code.

Shopify

  1. Go to Online Store → Themes → Customize and open the page or template for your COAs (create a page called "Lab results" first if you like).
  2. Add a Custom Liquid section (or a Custom HTML block, depending on your theme).
  3. Paste the snippet and save.

To show a product's COAs on its product page, open the embed with a search filled in by adding ?q= to the iframe's address, e.g. https://embed.coacomply.com/emb_YOUR_EMBED_ID?q={{ product.title | url_encode }}.

Settings in the URL

Add settings to the iframe's address to change one placement without touching the embed's saved settings: hide the search box on a product page, use the list layout in a sidebar, or match a page's colors. A URL setting wins over the saved one; anything invalid is ignored. Start with ?, join settings with &, and encode spaces as +.

A list with no search box, filtered to gummies
<iframe src="https://embed.coacomply.com/emb_YOUR_EMBED_ID?show_search=0&layout=list&accent_color=0F766E&q=gummies"
  … data-coacomply-embed></iframe>
Setting Values What it does
qtextOpen with this search filled in, e.g. a product name or batch.
statetwo-letter codeOpen filtered to one state.
categorycategory slugOpen filtered to one product category, e.g. gummies.
show_search1 or 0Show or hide the search box.
show_state_filter1 or 0Show or hide the state filter (shown only when COAs cover more than one state).
show_category_filter1 or 0Show or hide the product category filter (shown only with more than one category).
show_results_without_search1 or 00 keeps the list empty until someone searches, e.g. for a "find your batch" lookup.
layoutgrid, list or tableHow COAs are laid out.
headingtext, or empty to hideThe title above the list (up to 120 characters).
linksboth, certificate or pdfButtons on each COA: View COA and View PDF, or just one of them.
fieldsbrand, flavor, batch_number, state, category, reported_date, total_thc, overall_resultComma-separated details shown on each COA, in any order.
page_size6 to 60COAs loaded at a time (more load with "Show more").
background_colorhex, e.g. FFFFFFBackground color. Leave out the #, or write it as %23.
surface_colorhexCard and input background color.
text_colorhexMain text color.
muted_colorhexSecondary text color.
accent_colorhexButtons and links.
border_colorhexBorders and dividers.
font_familyfont name, e.g. InterBody font: one of the embed's own fonts or any Google Fonts family.
heading_font_familyfont nameHeading font (defaults to the body font).
base_font_size12 to 22Base text size in pixels.
heading_font_size16 to 56Heading size in pixels.
border_radius0 to 32Corner radius of cards, buttons and inputs, in pixels.
padding_x0 to 80Space at the left and right edges, in pixels.
padding_y0 to 80Space at the top and bottom edges, in pixels.

Custom CSS and your own font files can only be set in the dashboard.

With embed.js on the page, any input with data-coa-search searches the embed as people type, and selects with data-coa-state (two-letter state codes) or data-coa-category filter it. With several embeds on a page, put an embed's id in the attribute (data-coa-search="emb_…") to target just that one.

Search box + embed
<input type="search" data-coa-search placeholder="Search by product or batch">
<select data-coa-state>
  <option value="">All states</option>
  <option value="AZ">Arizona</option>
</select>

<iframe src="https://embed.coacomply.com/emb_YOUR_EMBED_ID" … data-coacomply-embed></iframe>
<script async src="https://embed.coacomply.com/embed.js"></script>

Going further: the API

Need complete control over the design?
The embed covers most sites. To build COA pages in your own components, show lab results on product pages, or sync COAs with your store or ERP, use the API: search COAs, read every field and get each COA's permanent page, PDF and QR links.

API

The REST API is the same one the dashboard uses: upload or import COAs, search them, read every extracted field, publish them, and get permanent landing page, PDF and QR code links.

  • Keys: create them in Settings → API & MCP (admins and owners). Each key can be read-only or read & write, and limited to some brands. The key is shown once; revoke it any time.
  • Auth: send Authorization: Bearer YOUR_API_KEY with every request to https://api.coacomply.com/v1.
  • Pagination, errors and limits: lists use cursor pagination (next_cursor), errors come back as { error: { code, message } }, and rate limit headers are on every response.
Find a COA by batch
curl https://api.coacomply.com/v1/coas/search?q=B126 \
  -H "Authorization: Bearer YOUR_API_KEY"

MCP for AI Agents

COA Comply runs a Model Context Protocol server, so AI agents can work with your COAs: find them by product, batch or lab, read results and links, check compliance, and, with read & write access, import COAs from lab links, fix their details and publish them.

  • Server: https://api.coacomply.com/mcp (Streamable HTTP).
  • Sign in (Claude, ChatGPT): add the server URL and connect. You sign in to COA Comply and choose the company, brands and access the app gets. An admin or owner approves it, and it appears in Settings → API & MCP, where you can disconnect it.
  • API key (Claude Code, Cursor and other clients that take headers): send Authorization: Bearer YOUR_API_KEY. The key decides what the assistant can see and change.
  • Tools: find_coas, get_coa, list_brands, compliance_summary, coa_compliance_pages, import_coas_from_urls, check_import, update_coa and publish_coa. Importing, updating and publishing need read & write access.
  1. In Claude (web or desktop), open Customize → Connectors, choose Add custom connector, name it COA Comply and use this URL:
    https://api.coacomply.com/mcp
  2. Choose Connect. Sign in to COA Comply, pick the brands and access Claude gets, and choose Allow.

On Team and Enterprise plans an owner adds the connector under Organization settings → Connectors; then each person connects it from Customize → Connectors.

More about what assistants can do on the MCP page. Ready to try it? Start a free trial.