Skip to content

Public REST API

MAC Cards Online exposes a REST API for two consumer profiles:

  • AI agents and integrations – list decks, draw random cards for self-reflection, fetch watermarked card images.
  • Shops and partners – generate, check, and revoke invite codes that grant deck access.

Both audiences talk to the same contract. Where the experience diverges, we call it out.

If you're building an AI agent

The minimal scenario – ChatGPT, a Python script, an MCP server, a Slack bot, anything that needs cards for associative or reflective work.

  1. Sign in to MAC Cards Online → profile menu → Access for AI and integrations (shop partners see the same page as «API Key»).
  2. Generate an mk_live_… key. Copy it immediately – it is shown only once.
  3. Import the AI-safe spec into ChatGPT or your tool of choice: https://app.makcards.online/api/v1/public/openapi.ai.json. This subset has no pearl-spending operations – the AI cannot accidentally issue a paid invite code on your behalf.
  4. First call:

    curl -X POST https://app.makcards.online/api/v1/public/draw \
      -H "Authorization: Bearer mk_live_..." \
      -H "Content-Type: application/json" \
      -d '{"scope":"any","count":1,"image_width":1024}'
    
  5. The response carries drawn[0].image_url – a signed link to the card PNG (15 minutes TTL). Fetch it with a plain GET, no extra auth: the token is embedded.

/draw parameters:

  • scope: deck (one deck, requires deck_id), any (across accessible decks), favorites (your favorites only).
  • count: 1..3 on Free, 1..5 on Premium. Each card counts as one unit in the rate-limit windows (batch accounting).
  • exclude_ids: array of {deck_id}:{card_id} references to avoid repeats inside a session.
  • image_width: 512 (default) or 1024.

If you're a shop or partner

The flow: you sell deck access on your own site, generate an invite code after payment, hand it to the buyer, the buyer redeems it inside MAC Cards Online. Business rules (refunds, pearl pricing, edge cases when a deck changes status) are documented in Pearls and Marketplace.

  1. Turn on payouts in pearls for each deck you sell – this drives the code-pricing logic.
  2. Generate a key in Access for AI and integrations, same place AI agents use.
  3. After the customer pays, mint a code:

    curl -X POST https://app.makcards.online/api/v1/public/invite-codes/generate \
      -H "Authorization: Bearer mk_live_..." \
      -H "Content-Type: application/json" \
      -d '{"deck_id":"<UUID>","access_type":"private","target_email":"buyer@example.com"}'
    
  4. Hand the code over to the buyer.

  5. If payment fails or a refund is due, revoke the code:

    curl -X POST https://app.makcards.online/api/v1/public/invite-codes/revoke \
      -H "Authorization: Bearer mk_live_..." \
      -H "Content-Type: application/json" \
      -d '{"code":"..."}'
    

    Pearls return to your balance as long as the code has not been activated yet.

Authentication

There are three ways to authenticate, in order of how most agents arrive:

  1. API keyAuthorization: Bearer mk_live_…. The same key works for every endpoint in this document. Manage it from Profile → Access for AI and integrations.
  2. MCP servers (hosted or local) – we run a Model Context Protocol bridge for Claude Desktop, ChatGPT plugins and similar clients. Pick the host that matches your account brand:

    • mcp.makcards.online (RU brand)
    • mcp.journalingapp.app (global brand)

    The hosted bridge accepts the same mk_live_… key. For air-gapped or local use, install the @maccards/mcp Node package and set MAKCARDS_API_KEY in its config. Both speak Streamable HTTP and expose four tools: list_decks, get_deck, draw_card, get_usage. 3. OAuth 2.1 – for web agents that want users to click "Connect" instead of pasting a key. ChatGPT.com and Claude.ai use this path. The user lands on our consent screen, sees exactly which scopes the agent is asking for (read, draw), and can revoke from Profile → Access for AI and integrations → Connected apps at any time. Token rotation is rolling; if a refresh token gets stolen and replayed, the whole chain is revoked and the user is notified.

Image endpoints also accept a short-lived signed token from /draw or /decks responses, supplied as either a ?token= query parameter or an X-Image-Token header. The token mechanism is unique to image endpoints – it isn't a substitute for any of the three above.

Treat any key or refresh token as a password. Never paste them into public repos, support chats, or screenshots. If you suspect compromise, regenerate the API key from Access for AI and integrations, or revoke the OAuth connection from Connected apps – the old credential stops working within a minute.

Rate limits

Window Free Premium
Draws per minute (draws_per_min) 30 120
Draws per hour (draws_per_hour) 200 2 000
Draws per day (draws_per_day) 500 10 000
/decks per minute 30 120
Image fetches per minute 60 240

Get your current consumption: GET /api/v1/public/usage. The response includes per-window counters plus by_endpoint_today – how many times you hit each endpoint family in the last 24 hours.

Responses carry RFC 9331 headers:

  • RateLimit-Limit – the tightest window currently in effect.
  • RateLimit-Remaining – units left.
  • RateLimit-Reset – seconds until the window resets.

On overage you get HTTP 429 with Retry-After. AI agents should just back off and retry.

Card images and watermark

All card images carry a watermark – the platform domain (makcards.online or journalingapp.app) is printed as a thin strip top and bottom at 6% opacity. Removing the watermark violates the terms.

Width is 512 or 1024 pixels. We never expose larger sizes through the API – it's part of the IP-protection for deck authors.

A deck card may look like a single image to the API even when copy-protection is enabled in the editor (storage is tile-split). The server reassembles it and applies the watermark on the fly.

License and usage

Each /draw card carries a license block:

  • type=user – UGC deck. Default: personal use yes, commercial use no, redistribution no.
  • type=community – public approved deck. Same defaults – personal use only.
  • type=invite_private / invite_public – access via a purchased invite code. The author's terms apply.

The effective_constraints block at the response level summarises the most-restrictive intersection across the drawn cards – useful for agents that need one decision point on «can I publish this spread».

Rule of thumb: even if your scenario is «help the user reflect», don't reuse the card art for marketing, don't publish individual cards as «wallpapers» or «daily content», don't ship bulk galleries. The platform tracks direct-image fetches via pub_api_draws; bulk-scrape pattern leads to account block.

Endpoint reference

Machine-readable spec:

  • Full: https://app.makcards.online/api/v1/public/openapi.json – for developers and shop partners.
  • AI-safe: https://app.makcards.online/api/v1/public/openapi.ai.json – for ChatGPT Custom GPTs and other AI agents that import one document. No invite-code operations here, so the chat user can't accidentally authorize pearl spending.

Quick table:

Endpoint Purpose Who uses
GET /me Caller profile, tier, locale both
GET /decks?scope=… List accessible decks both
GET /decks/{id} Deck details + groups + signed back_url both
POST /draw Draw random card(s) AI
GET /decks/{id}/cards/{cid}/image Watermarked card image AI
GET /decks/{id}/back Deck cover both
GET /decks/{id}/groups/{gid}/back Group cover both
GET /usage Current quotas both
POST /invite-codes/generate Mint a code shops
POST /invite-codes/apply Apply a code on behalf of a user shops
POST /invite-codes/check Look up a code shops
POST /invite-codes/revoke Revoke an unused code shops
GET /invite-codes List issued codes shops

Brand binding

Every API key, OAuth token and MCP session is bound to one brand – the host you made it on. A mk_live_… issued at app.makcards.online will return 403 if you point it at app.journalingapp.app and vice versa. Same applies to OAuth refresh tokens. There is no cross-brand session sharing: if you operate both brands, generate two keys and route each integration to its own host. The brand is auto-detected from the Host header, so just choose the right hostname and the rest takes care of itself.

Card leak forensics (moderation-only)

If a deck owner reports that a watermarked card is showing up where it shouldn't, the moderation team has a tool to identify which API caller drew it. The tool lives at POST /api/v1/public/mod/forensics/identify and is restricted to team-members. You upload the offending image, the server looks at the watermark pattern and returns the candidate API keys plus the draw timestamp. Pure read-only audit; not a self-serve endpoint.

If you found a leak, file it through Profile → Notifications → Send report or email moderation@makcards.online.

Error envelope

All error responses share one shape:

{
  "error": {
    "code": "deck_not_playable",
    "message": "This deck is not in a playable status.",
    "request_id": "019e3aa4-5c4d-709a-9606-9d9751461fd1"
  }
}
  • code – machine-readable identifier. Full set: unauthorized, invalid_api_key, invalid_token, forbidden, not_found, invalid_request, rate_limited, no_cards_available, deck_not_playable, content_blocked, payload_too_large, internal_error, service_unavailable.
  • request_id – UUID v7, matches the X-Request-Id response header. Quote it in a support ticket and we can find your exact request in seconds.

FAQ

Can I draw a card from a deck that's still a draft? No. Even the deck owner can't draw from a draft – you get 422 deck_not_playable. Publish it as private (or further) and the API starts serving it.

Why two OpenAPI documents? So AI tools (ChatGPT Custom GPTs especially) can't accidentally spend your pearls through /invite-codes/generate. The AI subset physically lacks those operations – the AI just doesn't know they exist.

I want only my own decks, without public or purchased ones. By default /decks returns everything you can read (scope=accessible): your own + approved-public + purchased. Pass ?scope=owned to scope the response to your own catalogue.

Can I get the original image without watermark? No. This is a deliberate IP-protection stance. The API always returns a watermarked PNG at 512 or 1024 pixels – with a white frame, rounded corners and a transparent background outside the frame so the card composites cleanly on any surface. The original is only available inside the playable board.

What if my key is compromised? Regenerate in Access for AI and integrations. The old key stops working within a minute.

Where to go next