sportsmarks/ docs

Sportsmarks API

Browse logos, read rulebooks, apply for licenses, submit products and report sales — over REST or MCP. Every capability is one action, exposed identically on both, so anything your agent can do over MCP you can also script over REST.

REST https://sportsmarks.com/api/v1MCP https://sportsmarks.com/api/mcpOpenAPI /api/v1/openapi.json

Authentication

There are two ways in. Either way, a credential always acts as one person in one org, with exactly that person's role and permissions.

Agents: connect over MCP (OAuth)
Recommended for Claude, ChatGPT, Cursor and other MCP clients. No keys to copy: the agent opens Sportsmarks, you sign in and approve what it can do.
  1. 1. Add Sportsmarks to your agent as a custom connector (MCP server) with this URL:
    https://sportsmarks.com/api/mcp
  2. 2. Approve it in the browser. Your agent opens Sportsmarks; sign in (or sign up your brand) and choose what it can do.
  3. 3. Ask it to get you licensed, for example:

    Use Sportsmarks to get my brand licensed: find the IP holders I want, read their rules, and apply for a License for each. Ask me before anything that needs my signature.

Scripts & integrations: API keys
Bearer keys for anything that speaks HTTP — CI jobs, storefront connectors, your own backend.
  1. Sign up (or sign in) and open Settings → API keys.
  2. Choose Create key, name it, and optionally narrow it to specific scopes.
  3. Copy the smk_… secret — it is shown once and stored only as a hash.

Send it on every request as Authorization: Bearer smk_…. The same header works on the MCP endpoint, so one key can serve both. Revoke any key instantly from the same page. Keys can also be managed over the API itself (POST /api/v1/api-keys).

A key acts in the org it was created in. If you belong to several orgs, pass X-Org-Id to act in another one you're a member of. Service keys (org-level, not tied to your login staying active) require an Owner/Admin and a human confirmation.

Quickstart: the logos API

Logos are the heart of Sportsmarks: each IP holder's marks, with stable IDs like BYU-0001, colorway variants, rates and license status. With no filters, list_logos returns each institution's primary logo.

# Who am I acting as?
curl -H "Authorization: Bearer smk_…" https://sportsmarks.com/api/v1/me

# Find a holder, list its logos, download one (URL is good for 10 minutes)
curl -H "Authorization: Bearer smk_…" "https://sportsmarks.com/api/v1/holders?q=texas"
curl -H "Authorization: Bearer smk_…" "https://sportsmarks.com/api/v1/logos?holder=TAMU"
curl -H "Authorization: Bearer smk_…" "https://sportsmarks.com/api/v1/logos/TAMU-001/download"

Lists are paged — follow next_offset when present. Over MCP the same capabilities are the tools search_holders, list_logos, get_logo and download_logo.

Conventions

Errors

Every error is { error: { code, message, details? } } with the matching HTTP status: 400 invalid input, 401 missing/invalid key, 403 not permitted for your role, 404 not found.

Human confirmations (202)

Legal and money actions (signing a license, rate changes, service keys) don't execute from the API. They return 202 with a preview and a confirm_url a human opens in the browser to approve.

Idempotency
Pass an Idempotency-Key header (REST) or idempotency_key argument (MCP) on writes you might retry.
Audit
Every write is recorded with who did it, over which channel (UI, REST, MCP) and with which key — visible to your org's admins.

Reference

Generated from the live action registry — the same registry that serves REST, MCP and the OpenAPI document, so this list can't drift. The MCP tool name equals the action name.

Logos

Action / MCP toolRESTDescription
list_logosGET/api/v1/logosLogos with ID (e.g. BYU-0002), name, rate, groups, a PNG preview_url, whether you may download the artwork, and variants (the same logo in other colorways, IDs like BYU-0002.v1). With no filters, each holder's primary logo (one per institution). Filter by holder code, q (name or ID text), logo group code, license (a License ID you're party to), or licensed_to_me (brands: logos covered by your Licenses in force) to search all logos; primary=true|false overrides either way. Returns a page (limit, offset) with total and next_offset. full adds download links, colors, usage notes and variant links; for artwork use download_logo.[org.read]
get_logoGET/api/v1/logos/:codeOne logo by its permanent ID, e.g. BYU-0002, with its variants.[org.read]
download_logoGET/api/v1/logos/:code/downloadThe original PNG or SVG of a logo, or of one of its variants (variant: 2 for BYU-0002.v2), for putting on product. Returns a url that downloads the file with no credentials for 10 minutes; inline=true adds the file as base64 instead (large for PNGs). Holders get their own logos; brands get logos a License in force covers, with all their variants. Every download is recorded in your audit log.[org.read]
create_logoPOST/api/v1/logosUpload one logo (PNG + SVG both required, base64-encoded) with a name and its own royalty rate (%). Returns the next permanent ID, e.g. BYU-0003. Optionally add it to existing groups.[logos.manage]
update_logoPATCH/api/v1/logos/:codeChange a logo's name, rate, files, colors, notes, or the groups it's in (group_ids replaces its membership; adding it to a group extends every License granting that group). primary: true makes it your primary logo, the one that stands for you in the catalog; the previous primary steps down. The ID never changes. Rate changes need human confirmation.[logos.manage]
retire_logoPOST/api/v1/logos/:code/retireRetire a logo. It keeps its ID forever so past sales still resolve, but can't be newly granted.[logos.manage]
add_logo_variantPOST/api/v1/logos/:code/variantsAdd the logo in another colorway (e.g. 'White' for a navy Block Y), as a PNG, an SVG, or both (base64). Variants are optional reference art for licensees: any License that grants the logo covers them, and they aren't approved on their own (product submission approves how they're used). Returns the variant ID, e.g. BYU-0004.v2.[logos.manage]
remove_logo_variantDELETE/api/v1/logos/:code/variants/:variantRemove one variant of a logo (variant: 2 for BYU-0004.v2). Its number is never reused.[logos.manage]
merge_logosPOST/api/v1/logos/:code/mergeFold logos that are really the same mark in other colorways into one logo: each logo in `merge` becomes a variant of `code` (BYU-0004.v2, named after the merged logo), keeping its PNG and SVG and any variants it had. The merged logos' own IDs stop existing and are never reused; `code` keeps its ID, rate and groups. Only logos nothing relies on yet can be merged: one that is on a License, an application, a product or your rulebook is refused. Needs human confirmation.[logos.manage]

Logo groups

Action / MCP toolRESTDescription
list_logo_groupsGET/api/v1/logo-groupsLogo groups (e.g. BYU-VINTAGE) with rate and member logo IDs. Filter by holder code; holders see their own by default.[org.read]
create_logo_groupPOST/api/v1/logo-groupsCreate a logo group with its own royalty rate, e.g. code VINTAGE → BYU-VINTAGE at 18%. A logo can be in several groups.[logos.manage]
update_logo_groupPATCH/api/v1/logo-groups/:codeRename, re-rate, freeze, or add/remove logo IDs. Adding a logo extends every License that grants this group unless the group is frozen. Rate changes need human confirmation.[logos.manage]

Holders & rulebooks

Action / MCP toolRESTDescription
search_holdersGET/api/v1/holdersIP holders open to licensing, with their code (logo ID prefix), active rulebook summary and fees. Search by q (name or code); returns a page (limit, offset) with total and next_offset.
get_rulebookGET/api/v1/holders/:holder/rulebookA holder's active rulebook: allowed grants (category × channel × territory with approval mode), open logos, fees, required docs and banned uses. Modes: auto (approved), soft_approve (approved now; the holder may object for soft_approval_window_days), holder_review (a person decides), soft_deny (denied, but you can appeal with appeal_denial), blocked (denied, final).
choose_templatePOST/api/v1/rulebook/templatePublish a new rulebook version from a template (small_school, governing_body, creator_brand), with optional field changes. Needs human confirmation.[rulebook.edit]
update_rulebookPATCH/api/v1/rulebookPublish a new rulebook version by changing fields of the active one (e.g. license_approval, default_royalty_rate, grants_allowed). Each rule's mode is one of auto (hard approval), soft_approve (approved now; you can object for soft_approval_window_days), holder_review, soft_deny (denied by default; the brand may appeal to you) or blocked (hard denial). Rulebook versions are immutable. Needs human confirmation unless delegated to agents.[rulebook.edit]
get_brand_kitGET/api/v1/holders/:holder/brand-kitEverything a licensor licenses out, in one call: logos (with PNG preview_url), phrases and taglines (their words), the groups they belong to, each one's royalty rate, the licensor's colors (hex, Pantone) and its usage guidelines. Brands also see licensed_to_you per mark. A kit the licensor made public is also readable with no sign-in at GET /api/v1/public/kits/<slug> and on the page public_url.[org.read]

Applications & licenses

Action / MCP toolRESTDescription
create_applicationPOST/api/v1/applicationsApply to a holder for a License: logo groups and/or logo IDs, channel types and territory groups. No categories needed: the License covers every product class the holder's rulebook allows, and Sportsmarks checks each product's category against the rulebook when it's submitted (pass categories only to ask for a narrower License). The holder's rulebook decides immediately: auto-approved (License issued, awaiting your signature), auto-denied with the failing rule IDs, or sent to the holder for review.[licenses.apply]
list_applicationsGET/api/v1/applicationsHolders: applications to you (filter status=holder_review for your review queue). Brands: your applications.[org.read]
get_applicationGET/api/v1/applications/:idOne application with its rule checks and resulting License, if any.[org.read]
decide_applicationPOST/api/v1/applications/:id/decideHolder's manual decision on an application in holder_review. Approving issues the License. Needs human confirmation.[licenses.decide]
list_licensesGET/api/v1/licensesHolders: Licenses granted to brands. Brands: Licenses you hold.[org.read]
get_licenseGET/api/v1/licenses/:idOne License with its logo access, scope, term, rate overrides and a short-lived agreement PDF link.[org.read]
sign_licensePOST/api/v1/licenses/:id/signSign a License awaiting signature. Always returns a preview and confirm link: a brand Owner must confirm in the browser. Sportsmarks then countersigns as the holder's agent and the License becomes active.[licenses.sign]
withdraw_applicationPOST/api/v1/applications/:id/withdrawTake back a License application that hasn't been decided yet (draft or waiting for the holder's review).[licenses.apply]
suspend_licensePOST/api/v1/licenses/:id/suspendPause an active License (e.g. unpaid royalties, an expired insurance certificate). While suspended, new products are denied and sales made from now on are charged at your maximum rate and flagged; sales made before stay licensed. Undo with reinstate_license. The brand sees the reason.[licenses.decide]
reinstate_licensePOST/api/v1/licenses/:id/reinstatePut a suspended License back in force. Sales made while it was suspended count as licensed again.[licenses.decide]
terminate_licensePOST/api/v1/licenses/:id/terminateEnd a License for good. Sales made from now on are unlicensed (charged at your maximum rate and flagged); sales made before stay licensed and royalties already owed stay owed. The brand may apply again. This can't be undone.[licenses.decide]
propose_amendmentPOST/api/v1/licenses/:license_id/amendmentsPropose a change to a signed License: renew it (renew: true), add logos or logo groups, change its channels or territories, set negotiated rates (rate_overrides) or move its end date. Nothing changes until the brand accepts (accept_amendment); one open proposal per License. Works on an active License, or an expired one to renew it.[licenses.decide]

Products

Action / MCP toolRESTDescription
submit_product_mockupPOST/api/v1/productsSubmit a product once with every logo ID on it (may span holders, e.g. UTAH-0001 + BYU-0002), its channels, the storefronts it will be listed on, and one SKU per variant. Either pass from_store (a product from list_store_products: its title, variants, SKUs and images fill in from your store) or pass images (base64 mockups) and variants yourself. Each SKU can belong to only one product. Sportsmarks checks the images, assigns the product its category from the title, product type, tags, description and images (pass category_hint only if those don't say what it is), creates one claim per holder and evaluates each against that holder's rulebook. Missing Licenses are drafted as bundled applications.[products.submit]
update_productPATCH/api/v1/products/:idChange a product's declared logo IDs, images, title or description (product_type, tags, category_hint: Sportsmarks re-categorizes it), channels, storefronts or variants and resubmit it. Use it to add an undeclared logo, drop a holder's marks, or fix what a holder flagged. To add a new size, pass add_variants: approvals stay as they are. A new look (color, design, material) of an approved product is refused (409): submit it as a colorway (submit_product_mockup with parent_product_id and look) so the holder sees its mockup. Claims already approved for an unchanged set of logos and unchanged images stay approved.[products.submit]
list_productsGET/api/v1/productsBrands: your products with their overall status. Filter by status (awaiting_license, pending, approved, denied, blocked).[org.read]
get_product_statusGET/api/v1/products/:idA product with its per-holder claim status. Brands see every claim, rate and holder note. A holder sees the product, the names of co-holders and the overall status, plus only its own claim.[org.read]
confirm_bundled_applicationsPOST/api/v1/products/:product_id/bundled-applications/confirmSubmit, in one step, the License applications Sportsmarks drafted for every holder on this product you aren't licensed with yet. Each holder's rulebook decides immediately; approved Licenses then need a brand Owner's signature (sign_license).[licenses.apply]
appeal_denialPOST/api/v1/products/:product_id/appealAsk a person at the holder to look at a product its rulebook denied by default (a claim with can_appeal: true in get_product_status). Say why it should be allowed in note. The claim goes to the holder's review queue; hard denials (blocked categories, logos or scope your License doesn't cover) can't be appealed: fix the product with update_product instead.[products.submit]

Sales & royalties

Action / MCP toolRESTDescription
get_sales_reporting_guideGET/api/v1/sales/guideStep-by-step instructions for an agent to report a month of sales: the exact Shopify GraphQL query to run with your own Shopify connector, the formats report_sales and upload_sales_csv accept, and how the period gets attested. Call this first.[org.read]
report_salesPOST/api/v1/salesSend sales lines for any period: format=shopify_orders with raw order nodes from your Shopify connector (GraphQL or REST shape), or format=lines with Sportsmarks lines (wholesale, Amazon, anything else). Each line is matched to an approved SKU and gets one royalty line per holder at the frozen rate; a sale outside a holder's Approval or License is charged at that holder's maximum royalty rate and flagged. Duplicates are ignored. Call get_sales_reporting_guide for the exact query.[sales.report]
upload_sales_csvPOST/api/v1/sales/uploadUpload sales as CSV/TSV text: Shopify's order export, Amazon's All Orders report, or the Sportsmarks template (see get_sales_reporting_guide). The format is detected from the header. Same matching, royalty lines and dedupe as report_sales.[sales.report]
list_sales_reportsGET/api/v1/sales/reportsYour monthly sales reports (draft or submitted) with totals.[reports.view]
get_sales_reportGET/api/v1/sales/reports/:periodOne period's sales: totals, royalties per holder, exceptions by rule, how the lines arrived, how many our storefront crawl corroborates, and approved listings that are live with no reported sales.[reports.view]
list_salesGET/api/v1/sales/reports/:period/linesSales lines in a period with their retailer and royalty lines; filter to exceptions (status=exception, with the rule that failed) or lines of SKUs declared not licensed (status=excluded).[reports.view]
submit_sales_reportPOST/api/v1/sales/reports/:period/submitAttest that a month's sales are complete and accurate and lock the period. Returns a preview with totals per holder; a brand Owner or Finance user confirms it in the browser (agents can't). A month with no sales can be submitted as $0.[sales.report]
exclude_skusPOST/api/v1/sales/excluded-skusSame as declare_not_licensed, by SKU. Declare that SKUs you sold carry no licensed marks (e.g. a plain wallet), so their sales are left out of royalties without an unknown-SKU exception. Stays declared for later months, is shown in each month's attestation, and is audited. A SKU already on one of your products can't be excluded; adding an excluded SKU to a product later undoes it.[sales.report]

Catalog & links

Action / MCP toolRESTDescription
list_channel_variantsGET/api/v1/catalog/variantsThe variants of the store products you sync, as each channel knows them (Shopify variants, Amazon seller SKUs, wholesale lines), plus SKUs that appeared on your sales reports, and which of your product variants each one is. Linked automatically when the SKU or barcode matches; otherwise unlinked, with suggestions to confirm (accept_link_suggestions), link by hand (link_variant) or declare not licensed (declare_not_licensed). Unlinked items with sales block submitting that month.[org.read]
link_variantPOST/api/v1/catalog/variants/:channel_variant_id/linkSay which of your product variants a channel variant is (e.g. the Amazon seller SKU USAH-TEE-M-AMZ is your Navy Tee, size M). Its sales then count as that product's: its sales in open (draft) months are re-checked (including ones counted under a product it was linked to before), and listings re-matched. Items whose SKU or barcode matches are linked automatically and can't be re-linked here.[products.submit]
accept_link_suggestionsPOST/api/v1/catalog/suggestions/acceptLink each of these unlinked channel variants to its top suggestion (see list_channel_variants). Items with no suggestion are skipped and reported.[products.submit]
unlink_variantPOST/api/v1/catalog/variants/:channel_variant_id/unlinkUndo a link you made (or a not-licensed declaration). Its sales in open (draft) months are re-checked and become exceptions until it's linked again; submitted months don't change. Links made by a matching SKU or barcode can't be undone here: change the product's variants instead.[products.submit]
declare_not_licensedPOST/api/v1/catalog/not-licensedDeclare that these channel variants carry no licensed marks (e.g. a plain wallet), so their sales are left out of royalties without an exception. Applies to the SKU everywhere you sell it, stays declared for later months, is shown in each month's attestation, and is audited. Undo with unlink_variant.[sales.report]

API keys

Action / MCP toolRESTDescription
create_api_keyPOST/api/v1/api-keysCreate a personal API key for agents and integrations, acting as you in your current org. Optional scopes narrow it to a subset of your permissions. Service keys (service: true) need users.manage and human confirmation. The key is shown once.[org.read]
list_api_keysGET/api/v1/api-keysYour API keys and connected agents (OAuth, with oauth_client_name) in the current org, and the org's service keys if you are an Owner/Admin. Secrets are never returned.[org.read]
revoke_api_keyDELETE/api/v1/api-keys/:idRevoke one of your API keys or connected agents (or a service key, if you are an Owner/Admin). Takes effect immediately.[org.read]

Confirmations

Action / MCP toolRESTDescription
get_confirmationGET/api/v1/confirmations/:tokenThe preview and status of a confirmation token returned by a legal or money action.[org.read]
list_pending_confirmationsGET/api/v1/confirmationsActions in your org waiting for a human to confirm (signatures, rate changes, rulebook publishes, manual approvals).[org.read]
confirm_actionPOST/api/v1/confirmations/confirmExecute a previewed action. Legal and money actions need a signed-in human in the browser (agents get 403) unless an Owner delegated that action to agents (get_agent_delegation). Product decisions (decide_review) and delegated actions may be confirmed by the same agent key that requested them.[org.read]
decline_actionPOST/api/v1/confirmations/declineCancel a pending confirmation without executing it.[org.read](browser only)

Account & org

Action / MCP toolRESTDescription
whoamiGET/api/v1/meThe calling user, the org and role this call acts in, its permissions, and every org membership.
whats_nextGET/api/v1/whats-nextThe next steps for your org right now, each with the action (tool) that does it.[org.read]
list_membersGET/api/v1/membersUsers in the caller's org with their roles, plus open invites.[org.read]
invite_memberPOST/api/v1/invitesInvite someone to the org with a role. Emails them a one-time link (also returned as invite_url); only that email can accept it, and accepting creates their account if they don't have one. Inviting an Owner needs human confirmation.[users.manage]
accept_invitePOST/api/v1/invites/acceptJoin an org using an invite token. The signed-in user's email must match the invite.(browser only)
complete_brand_signupPOST/api/v1/signups/:signup_id/completeAfter the person confirmed their email from the sign-up link: create the brand and make them its Owner. Only the signed-in person whose email started the sign-up can finish it, in the browser.(browser only)
update_memberPATCH/api/v1/members/:user_idChange a member's role. Making someone an Owner needs human confirmation.[users.manage]
remove_memberDELETE/api/v1/members/:user_idRemove a user from the org. Every API key and agent acting for them in this org stops working immediately.[users.manage]
get_taxonomyGET/api/v1/taxonomyThe shared product classes (categories: apparel, headwear, accessories, home, toys-games, sporting-goods, paper-goods, pet, automotive, with a few subcategories like apparel/tops/t-shirt), channel types and territory groups used by every holder. Licenses and rulebooks work on these classes. Each product also gets an exact Sportsmarks category (search_product_categories) and attributes like age-group, which Sportsmarks assigns when it's submitted; holders' product_exceptions target those.
search_product_categoriesGET/api/v1/product-categoriesSearch the Sportsmarks product categories (built from Shopify's open taxonomy v2026-08, plus licensing categories like Replica Jerseys and Pennants), e.g. "t-shirt", "shot glass", "replica jersey". Each result shows its product class (null = not licensable) and whether Sportsmarks added it. Use an id in a rulebook's product_exceptions. You don't need one to submit a product: Sportsmarks categorizes it.
list_rulebook_templatesGET/api/v1/rulebook-templatesTemplate rulebooks a holder can start from: Small School, Governing Body, Creator Brand.
update_brand_kitPATCH/api/v1/brand-kitMake your brand kit public or private, and set its usage guidelines. Public: anyone can see your marks' names, PNG previews, phrases, colors, rates and guidelines without signing in (a page, and GET /api/v1/public/kits/<slug>). Artwork files stay available only to brands licensed for them.[logos.manage]
set_brand_colorsPUT/api/v1/brand-kit/colorsReplace your brand kit's color palette: each color has a name, a hex value, an optional Pantone reference and a role (primary, secondary, accent, neutral). Pass the full list in display order ([] clears it). Licensees and, if your kit is public, anyone can see it.[logos.manage]
create_phrasePOST/api/v1/phrasesAdd words you license (a phrase, slogan, tagline or nickname, e.g. "Go Utes!") as their own mark with a royalty rate (%). It gets the next permanent ID like a logo (e.g. UTAH-0031), can be put in groups, granted on a License on its own, declared on products, and is looked for on product images by its words. Edit it later with update_logo; retire it with retire_logo.[logos.manage]
upload_documentPOST/api/v1/documentsPut a document on file for your brand: coi (certificate of insurance), code_of_conduct, w9 or factory_disclosure. Licensors' rulebooks can require these (required_docs) before a License is issued; a missing one auto-denies the application with REQUIRED_DOC_MISSING. PDF, PNG or JPEG, base64, up to 10 MB. Uploading the same kind again replaces it. Pass expires_on (e.g. the policy end date) so it stops counting once it lapses.[licenses.apply]
list_documentsGET/api/v1/documentsDocuments on file, with short-lived download links. Brands: your own. Licensors: pass brand_id to see the documents of a brand that has applied to you or holds your License.[org.read]
remove_documentDELETE/api/v1/documents/:idTake a document off file. Licenses already issued are unaffected; new applications that require it will be denied until you upload another.[licenses.apply]
list_amendmentsGET/api/v1/amendmentsAmendments and renewals proposed on your Licenses (status=proposed for the ones waiting on the brand).[org.read]
accept_amendmentPOST/api/v1/amendments/:id/acceptAgree to the licensor's proposed amendment or renewal. The License changes at once and a new agreement PDF is issued. Like signing, a brand Owner confirms it in the browser unless your org delegated it to agents.[licenses.sign]
decline_amendmentPOST/api/v1/amendments/:id/declineTurn down the licensor's proposed amendment or renewal. The License stays as it is.[licenses.sign]
withdraw_amendmentPOST/api/v1/amendments/:id/withdrawTake back an amendment or renewal you proposed that the brand hasn't accepted yet.[licenses.decide]
quote_ratePOST/api/v1/quote-rateGiven the logo IDs on a product (may span holders, e.g. UTAH-0004 + BYU-0002), return each holder's rate and its source under your Licenses, the total stacked rate, and any holders you still need a License from.[org.read]
export_audit_logGET/api/v1/audit-logAudit entries for your org, newest first: who (user or system), role, channel (ui/rest/mcp), API key, action, target and before/after.[audit.view]
add_storefrontPOST/api/v1/storefrontsDeclare a Shopify or Amazon storefront URL where you sell (e.g. ooopit.com or an Amazon brand store). Returns how to prove ownership: a DNS TXT record or a meta tag.[channels.connect]
verify_storefrontPOST/api/v1/storefronts/:id/verifyCheck the storefront's DNS TXT record or home-page meta tag and mark it verified.[channels.connect]
list_storefrontsGET/api/v1/storefrontsYour declared storefronts with their ownership status and verification instructions.[org.read]
add_wholesale_accountPOST/api/v1/wholesale-accountsDeclare a wholesale buyer (campus store, boutique, distributor…). Buyer details are optional unless a holder's rulebook asks for them.[channels.connect]
list_wholesale_accountsGET/api/v1/wholesale-accountsYour declared wholesale buyers.[org.read]
list_store_productsGET/api/v1/store-productsProducts in your Shopify store (read live from its public product feed; nothing is stored) with every variant's SKU, images and a suggested category, whether each is already submitted, whether you sync it, and why we'd recommend syncing it (a variant SKU or barcode is on one of your products, the title looks like one, or it names one of your licensors). status=recommended lists the ones worth syncing, best first: sync them with sync_store_products. Or pick one and pass it to submit_product_mockup as from_store so its title, variants, SKUs and images fill in automatically (that also syncs it). Agents with a Shopify connector can instead pass variants and images directly.[org.read]
list_reviewsGET/api/v1/reviewsYour product claims: the products carrying your marks that your rulebook sent to you (status=holder_review, the default), the ones it approved provisionally that you can still object to (status=provisional, with objection_until), or any other status. Shows co-holder names, never their terms.[reports.view]
decide_reviewPOST/api/v1/reviews/:product_id/decideYour decision on a product claim waiting for you (holder_review), or your objection to one your rulebook approved provisionally (list_reviews status=provisional, until its objection_until). approve freezes the rate on an Approval tied to the product's SKUs (on a provisional approval it ends the objection window early). deny blocks the product. request_changes sends it back to the brand to fix and resubmit; it returns to you afterwards. deny and request_changes show the brand your note; an objection keeps sales made up to now licensed. Returns a preview and confirm_token first; the same agent may then call confirm_action with that token.[products.decide]
get_royalty_summaryGET/api/v1/royaltiesRoyalties by period. Brands: per holder, including draft periods. Holders: per licensee, from submitted reports only, and only your own royalty lines.[reports.view]
import_listingsPOST/api/v1/listingsSync the store products you choose (e.g. licensed products from your Shopify admin, Amazon ASINs, wholesale styles): channel, the channel's product ID, title, URL and SKUs, or better its variants (with barcode/ASIN/options). Send only the products you want tracked, not your whole catalog. Each is kept as a listing and its variants on Catalog & links, linked to your product variants by SKU or barcode. One that's live before every holder approved, or has variant SKUs not on the product, raises a flag. Stop syncing one with unsync_listing.[products.submit]
crawl_storefrontPOST/api/v1/storefronts/:storefront_id/crawlRe-read a Shopify storefront's public product feed (no credentials) and refresh the products you sync: new variants, removed ones, delisted products. Nothing else in the store is stored; recommended says how many products you don't sync look licensed (see list_store_products). Synced products live before every holder approved, or with variant SKUs not on the product, raise flags; sales of SKUs seen live are marked corroborated. Runs nightly too.[products.submit]
sync_store_productsPOST/api/v1/storefronts/:storefront_id/syncStart syncing the store products you choose (by the store's product ID, from list_store_products). Each is kept as a listing, its variants go on Catalog & links (linked to your product variants by SKU or barcode), and from then on it's refreshed nightly and its sales are tracked. Nothing else in the store is synced.[products.submit]
unsync_listingPOST/api/v1/listings/:id/unsyncStop syncing a store product: it's no longer refreshed and its sales from a connected store aren't imported. Its variants that never sold are removed from Catalog & links (ones with sales stay, with their history); its open flags close with that reason. Sync it again any time.[products.submit]
list_listingsGET/api/v1/listingsThe store products you sync and their status: approved, not_approved (live before every holder approved), unmatched (not linked to one of your products yet: link its variants on Catalog & links) or dismissed.[org.read]
dismiss_listingPOST/api/v1/listings/:id/dismissMark a live listing as carrying no licensed marks (e.g. a blank tee), with a reason. Closes its flags; the reason is kept and audited.[products.submit]
list_flagsGET/api/v1/flagsCompliance flags: listings live before every holder approved, listings with variant SKUs not on the product, and sales that fail the sales gate. Brands see all of theirs; a holder sees only flags involving its own marks.[reports.view]
resolve_flagPOST/api/v1/flags/:id/resolveClose an open compliance flag on your marks with a note: resolved (dealt with, e.g. you approved the product or settled it with the brand) or dismissed (not a problem). The brand sees the note. The royalty already charged on a flagged sale doesn't change. Flags also close on their own when the cause is fixed.[compliance.flag]
list_royalty_linesGET/api/v1/royalties/:period/linesEvery sale line that owes you a royalty in one month, from reports the brands have submitted: licensee, order, SKU, product, retailer, units, net sales, the rate applied and why, and whether the sale was outside your License (unlicensed, charged at your maximum rate). Only your own royalty lines. Returns a page with total and next_offset.[reports.view]
list_statementsGET/api/v1/statementsWhat brands owe licensors: one royalty statement per month and licensor (issued when the brand's sales report is submitted, net of credits and the unearned advance), plus each License's application fee, annual fees, advance and any minimum-guarantee shortfall. Brands see what they owe; licensors see what they are owed. status: open (owed), processing, paid, failed, credit (a month where refunds exceeded sales; used against later months), applied, void. Open statements are collected by bank debit (ACH) when due, once the brand has authorized a bank account (start_bank_setup) and the licensor has set up payouts (start_payout_setup); see get_payment_setup.[reports.view]
get_payment_setupGET/api/v1/payments/setupWhether your org is ready for payments. Brands: the bank account authorized for debits (statements are collected by bank debit only; no cards). Licensors: whether the payout account is set up and Stripe can pay you. Says what to do next if not.[reports.view]
start_bank_setupPOST/api/v1/payments/bank-setupGet a link to a Stripe page where a person at your brand links a US bank account and accepts the debit authorization. Statements are then collected from that account automatically when due. Replaces any bank account already on file. Bank accounts only; cards aren't accepted. The link is for the person who asked for it and expires.[statements.pay]
start_payout_setupPOST/api/v1/payments/payout-setupGet a link to Stripe's onboarding where a person at your organization enters its legal details and the bank account royalties are paid to. Run it again to finish or update. The link is for the person who asked for it and expires in minutes.[billing.request]
pay_statementPOST/api/v1/statements/:id/payStart the bank debit for one open or failed statement now instead of waiting for its due date (open statements are collected automatically when due). The statement is processing until the bank confirms, usually within about four business days. A person confirms it in the browser unless your org delegated it to agents.[statements.pay]
list_eventsGET/api/v1/eventsWhat happened in your org, oldest first: application.submitted, application.decided, application.withdrawn, license.issued, license.signed, license.status_changed, amendment.proposed, amendment.decided, product.review_needed, product.decided, product.appealed, flag.opened, flag.resolved, sales_report.submitted, rulebook.published, statement.issued, statement.paid, statement.failed. Each event has an id, type, created_at and a small data object with IDs and statuses (fetch detail with the usual actions). Pass after (the last id you saw) to get only newer ones; poll this, or register a webhook (create_webhook) to be told instead.[org.read]
create_webhookPOST/api/v1/webhooksHave Sportsmarks POST your org's events to an https URL as they happen (JSON: id, type, created_at, data). Returns a signing secret once: each request carries a Sportsmarks-Signature header, t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>" with the secret>. Answer 2xx within 5 seconds; failures are retried for about two days. Leave event_types empty for every type.[users.manage]
list_webhooksGET/api/v1/webhooksYour org's webhook endpoints with each one's last deliveries (status, attempts, HTTP status, error).[users.manage]
delete_webhookDELETE/api/v1/webhooks/:idStop sending events to a webhook endpoint and remove it.[users.manage]
list_orgsGET/api/v1/admin/orgsEvery holder and brand on Sportsmarks with its members, open invites and whether a rulebook is active.[platform.admin]
create_holder_orgPOST/api/v1/admin/holdersCreate a school, governing body or creator (holder) or an athlete as an IP holder. The code is the permanent logo-ID prefix (BYU → BYU-0001). Then invite its first Owner with invite_org_owner.[platform.admin]
set_platform_ratePOST/api/v1/admin/orgs/:org_id/platform-rateSet Sportsmarks' own terms with one licensor, as negotiated with it: the rate added on top of its royalty (e.g. 2 on a licensor charging 12% means its brands pay 14% of net sales and the licensor keeps 12%) and, optionally, Sportsmarks' own application and annual fees on top of the licensor's. Pass only what changes. Brands see the combined figures. The terms are fixed on each License when it is issued and stated in its agreement, so a change applies to new Licenses and to renewals, not to Licenses already issued. A human confirms it in the browser.[platform.admin]
invite_org_ownerPOST/api/v1/admin/orgs/:org_id/owner-invitesInvite the first Owner of an IP holder (or the athlete of an athlete holder). They get an email; only that address can accept, and accepting creates their account.[platform.admin]
get_agent_delegationGET/api/v1/agent-delegationThe legal and money actions in your org that normally need a human to confirm in the browser, and which of them an Owner has delegated to agents. A delegated action still returns a preview and confirm_token; the agent that requested it may then confirm it.[org.read]
set_agent_delegationPOST/api/v1/agent-delegationSet the full list of confirm-gated actions your org's agents may complete without a human in the browser (see get_agent_delegation for the choices; pass [] to delegate nothing). Owners only, and always confirmed by a human in the browser.[users.manage]
Machine-readable spec
The full OpenAPI 3.1 document, with input schemas for every endpoint, lives at /api/v1/openapi.json. MCP clients get the same schemas from the server's tool listing at https://sportsmarks.com/api/mcp.