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.
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.
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
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 tool | REST | Description |
|---|---|---|
| list_logos | GET/api/v1/logos | Logos 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_logo | GET/api/v1/logos/:code | One logo by its permanent ID, e.g. BYU-002, with its variants.[org.read] |
| download_logo | GET/api/v1/logos/:code/download | The 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_logo | POST/api/v1/logos | Upload 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_logo | PATCH/api/v1/logos/:code | Change 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_logo | POST/api/v1/logos/:code/retire | Retire a logo. It keeps its ID forever so past sales still resolve, but can't be newly granted.[logos.manage] |
| add_logo_variant | POST/api/v1/logos/:code/variants | Add 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_variant | DELETE/api/v1/logos/:code/variants/:variant | Remove one variant of a logo (variant: 2 for BYU-004.v2). Its number is never reused.[logos.manage] |
Logo groups
| Action / MCP tool | REST | Description |
|---|---|---|
| list_logo_groups | GET/api/v1/logo-groups | Logo 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_group | POST/api/v1/logo-groups | Create 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_group | PATCH/api/v1/logo-groups/:code | Rename, 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 tool | REST | Description |
|---|---|---|
| search_holders | GET/api/v1/holders | IP 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_rulebook | GET/api/v1/holders/:holder/rulebook | A holder's active rulebook: allowed grants (category × channel × territory with approval mode), open logos, fees, required docs and banned uses. |
| choose_template | POST/api/v1/rulebook/template | Publish a new rulebook version from a template (small_school, governing_body, creator_brand), with optional field changes. Needs human confirmation.[rulebook.edit] |
| update_rulebook | PATCH/api/v1/rulebook | Publish 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 tool | REST | Description |
|---|---|---|
| create_application | POST/api/v1/applications | Apply 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_applications | GET/api/v1/applications | Holders: applications to you (filter status=holder_review for your review queue). Brands: your applications.[org.read] |
| get_application | GET/api/v1/applications/:id | One application with its rule checks and resulting License, if any.[org.read] |
| decide_application | POST/api/v1/applications/:id/decide | Holder's manual decision on an application in holder_review. Approving issues the License. Needs human confirmation.[licenses.decide] |
| list_licenses | GET/api/v1/licenses | Holders: Licenses granted to brands. Brands: Licenses you hold.[org.read] |
| get_license | GET/api/v1/licenses/:id | One License with its logo access, scope, term, rate overrides and a short-lived agreement PDF link.[org.read] |
| sign_license | POST/api/v1/licenses/:id/sign | Sign 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 tool | REST | Description |
|---|---|---|
| submit_product_mockup | POST/api/v1/products | Submit 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_product | PATCH/api/v1/products/:id | Change 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_products | GET/api/v1/products | Brands: your products with their overall status. Filter by status (awaiting_license, pending, approved, denied, blocked).[org.read] |
| get_product_status | GET/api/v1/products/:id | A 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_applications | POST/api/v1/products/:product_id/bundled-applications/confirm | Submit, 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 tool | REST | Description |
|---|---|---|
| get_sales_reporting_guide | GET/api/v1/sales/guide | Step-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_sales | POST/api/v1/sales | Send 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_csv | POST/api/v1/sales/upload | Upload 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_reports | GET/api/v1/sales/reports | Your monthly sales reports (draft or submitted) with totals.[reports.view] |
| get_sales_report | GET/api/v1/sales/reports/:period | One 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_sales | GET/api/v1/sales/reports/:period/lines | Sales 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_report | POST/api/v1/sales/reports/:period/submit | Attest 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_skus | POST/api/v1/sales/excluded-skus | 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] |
API keys
| Action / MCP tool | REST | Description |
|---|---|---|
| create_api_key | POST/api/v1/api-keys | Create 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_keys | GET/api/v1/api-keys | Your 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_key | DELETE/api/v1/api-keys/:id | Revoke 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 tool | REST | Description |
|---|---|---|
| get_confirmation | GET/api/v1/confirmations/:token | The preview and status of a confirmation token returned by a legal or money action.[org.read] |
| list_pending_confirmations | GET/api/v1/confirmations | Actions in your org waiting for a human to confirm (signatures, rate changes, rulebook publishes, manual approvals).[org.read] |
| confirm_action | POST/api/v1/confirmations/confirm | Execute 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_action | POST/api/v1/confirmations/decline | Cancel a pending confirmation without executing it.[org.read](browser only) |
Account & org
| Action / MCP tool | REST | Description |
|---|---|---|
| whoami | GET/api/v1/me | The calling user, the org and role this call acts in, its permissions, and every org membership. |
| whats_next | GET/api/v1/whats-next | The next steps for your org right now, each with the action (tool) that does it.[org.read] |
| list_members | GET/api/v1/members | Users in the caller's org with their roles, plus open invites.[org.read] |
| invite_member | POST/api/v1/invites | Invite 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_invite | POST/api/v1/invites/accept | Join an org using an invite token. The signed-in user's email must match the invite.(browser only) |
| complete_brand_signup | POST/api/v1/signups/:signup_id/complete | After 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_member | PATCH/api/v1/members/:user_id | Change a member's role. Making someone an Owner needs human confirmation.[users.manage] |
| remove_member | DELETE/api/v1/members/:user_id | Remove a user from the org. Every API key and agent acting for them in this org stops working immediately.[users.manage] |
| get_taxonomy | GET/api/v1/taxonomy | The 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_categories | GET/api/v1/product-categories | Search 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_templates | GET/api/v1/rulebook-templates | Template rulebooks a holder can start from: Small School, Governing Body, Creator Brand. |
| quote_rate | POST/api/v1/quote-rate | Given 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_log | GET/api/v1/audit-log | Audit 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_storefront | POST/api/v1/storefronts | Declare 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_storefront | POST/api/v1/storefronts/:id/verify | Check the storefront's DNS TXT record or home-page meta tag and mark it verified.[channels.connect] |
| list_storefronts | GET/api/v1/storefronts | Your declared storefronts with their ownership status and verification instructions.[org.read] |
| add_wholesale_account | POST/api/v1/wholesale-accounts | Declare a wholesale buyer (campus store, boutique, distributor…). Buyer details are optional unless a holder's rulebook asks for them.[channels.connect] |
| list_wholesale_accounts | GET/api/v1/wholesale-accounts | Your declared wholesale buyers.[org.read] |
| list_store_products | GET/api/v1/store-products | Products 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_reviews | GET/api/v1/reviews | Your 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_review | POST/api/v1/reviews/:product_id/decide | Your 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_summary | GET/api/v1/royalties | Royalties by period. Brands: per holder, including draft periods. Holders: per licensee, from submitted reports only, and only your own royalty lines.[reports.view] |
| import_listings | POST/api/v1/listings | Tell 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_storefront | POST/api/v1/storefronts/:storefront_id/crawl | Read 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_listings | GET/api/v1/listings | Your 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_listing | POST/api/v1/listings/:id/dismiss | Mark 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_flags | GET/api/v1/flags | Compliance 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_orgs | GET/api/v1/admin/orgs | Every holder and brand on Sportsmarks with its members, open invites and whether a rulebook is active.[platform.admin] |
| create_holder_org | POST/api/v1/admin/holders | Create 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_owner | POST/api/v1/admin/orgs/:org_id/owner-invites | Invite 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] |