SSportsmarks/ 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-001, 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-002), name, rate, groups, a PNG preview_url, whether you may download the artwork, and variants (the same logo in other colorways, IDs like BYU-002.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-002, 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-002.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-003. 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-004.v2.[logos.manage]
remove_logo_variantDELETE/api/v1/logos/:code/variants/:variantRemove one variant of a logo (variant: 2 for BYU-004.v2). Its number is never reused.[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.
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). Rulebook versions are immutable. Needs human confirmation.[rulebook.edit]

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]

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-001 + BYU-002), 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 or color, pass add_variants: approvals stay as they are. 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]

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-skusDeclare 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]

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). Manual product decisions (decide_review) 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.
quote_ratePOST/api/v1/quote-rateGiven the logo IDs on a product (may span holders, e.g. UTAH-004 + BYU-002), 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 from its public product feed) with every variant's SKU, images and a suggested category, and whether each is already submitted. Pick one and pass it to submit_product_mockup as from_store so its title, variants, SKUs and images fill in automatically. 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) 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 in holder_review. Approving freezes the rate on an Approval tied to the product's SKUs; denying blocks the product and tells the brand why. Returns a preview and confirm_token first; the same agent may confirm it with confirm_action.[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/listingsTell Sportsmarks what you have listed for sale (e.g. products from your Shopify connector, Amazon ASINs, wholesale styles): channel, the channel's product ID, title, URL and SKUs. Only listings whose SKUs are on one of your products are kept (the rest are counted as ignored). One that's live before every holder approved, or has variant SKUs not on the product, raises a flag.[products.submit]
crawl_storefrontPOST/api/v1/storefronts/:storefront_id/crawlRead a Shopify storefront's public product feed (no credentials) and keep the products whose SKUs are on one of your products as listings; everything else is ignored. Listings 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]
list_listingsGET/api/v1/listingsYour listings of submitted products and their status: approved, not_approved (live before every holder approved) or dismissed. unmatched lists ones that no longer match a product's SKUs (not tracked).[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]
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-001). Then invite its first Owner with invite_org_owner.[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]
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.