Custom GPT trivia tutor with QuizBase OpenAPI action#
A Custom GPT is the OpenAI-flavoured version of Claude.ai Projects: a persistent assistant with custom instructions, attached files, and Actions (OpenAI’s name for external HTTP tools). Wire QuizBase as an Action, paste a tuning prompt, and you have a conversational trivia tutor that picks age-appropriate questions, evaluates answers, and explains topics on misses — all in the ChatGPT mobile app.
Total time end-to-end: about ten minutes. No code (ChatGPT speaks OpenAPI natively, and QuizBase already publishes one at /openapi.json), no deploy, no infra. The result is a chat persona kids or learners can talk to in plain language: “give me an animals question for an 8-year-old”.
What you’ll build#
A Custom GPT called “Trivia Tutor” with:
- OpenAPI action pointing to
https://quizbase.runriva.com/openapi.json— ChatGPT discovers every public endpoint automatically - Bearer authentication via your
qb_pk_*publishable key - System prompt that constrains questions to age-appropriate categories (
?category=animals,sports,general-knowledge,?difficulty=easy) - Conversation flow — asks one question, waits for user answer, evaluates, explains the topic in friendly terms on a miss, asks if they want another
- Stable IDs for re-asking — when the user says “ask me yesterday’s question again”, the GPT uses
/api/v1/questions/{id}to fetch the exact same question by theidit remembered
The mechanic — IDs for follow-up retrieval#
Custom GPT context is per-conversation. If the user closes the chat and reopens, the GPT can’t “remember” yesterday’s question by text — model context resets. But your system prompt can include a Memory directive that asks the GPT to surface question.id after each round (“the question id was 01HXY... if you want me to revisit it”). When the user pastes the ID back tomorrow, the GPT calls the getQuestionById action with that UUID — exact same question text, correct answer, attribution. See /docs/api/questions-by-id for the endpoint reference. Stable IDs make conversation continuity work across sessions without any backend.
The same pattern is documented at /docs/api/questions-by-id and used by the MCP Slack trivia bot for per-channel dedup.
Step 1 — Get a QuizBase publishable key#
In your QuizBase dashboard, create a qb_pk_* key. Publishable keys are the right kind here — Custom GPTs run on OpenAI’s servers and call your API on every user message, but the key never appears in the chat (ChatGPT keeps it server-side). Free tier 500 req/day is enough for personal use; a public GPT shared with hundreds of users wants the paid tier.
No account yet? Sign up — free tier, no card.
Step 2 — Create a Custom GPT in ChatGPT#
In ChatGPT (Plus or Team plan required for Custom GPTs), click your avatar → My GPTs → Create a GPT. Two tabs: Create (conversational builder) and Configure (manual fields). Switch to Configure.
- Name: Trivia Tutor
- Description: “A conversational trivia coach that asks you one question at a time, explains the topic on misses, and adapts category and difficulty to your level.”
- Instructions: (system prompt — paste in Step 4)
- Conversation starters: Add 3 — “Quiz me on animals”, “Give me a medium-difficulty geography question”, “Ask me yesterday’s question again”
Don’t save yet; wire the Action first.
Step 3 — Add the QuizBase OpenAPI action#
In the Configure tab, scroll to Actions → Create new action. Two fields matter:
- Authentication: API Key → Bearer → paste your
qb_pk_*key. ChatGPT stores it encrypted; users of your GPT never see it. - Schema: ChatGPT accepts an OpenAPI 3.x URL or pasted JSON. Use the URL field:
https://quizbase.runriva.com/openapi.json. ChatGPT fetches the spec, validates, and lists every public endpoint.
Hit Save. The action appears under your GPT with operations like getQuestionsRandom, getQuestionById, getTopics, getCategories. ChatGPT picks the right one based on user phrasing — “give me a question” → getQuestionsRandom, “ask me question 01HXY…” → getQuestionById.
Step 4 — Wire system instructions for the conversation flow#
Paste this into the Instructions field (replaces or appends to whatever you typed in Step 2):
You are a friendly trivia tutor. Default settings: difficulty=medium,
language=en, mixed categories. If the user mentions an age (e.g. "for my
8-year-old"), constrain category to animals,sports,general-knowledge and
difficulty to easy.
Conversation flow:
1. Call getQuestionsRandom with the user's filters. Receive {id, text,
correctAnswer, incorrectAnswers, attribution}.
2. Present the question and shuffle four answer choices (A-D) — pick one
letter as correct based on the position you place correctAnswer.
3. Wait for the user's letter or written answer.
4. Reveal the correct answer. ALWAYS include the source line:
"Source: {attribution.author} ({attribution.license})"
5. On a miss, give a one-paragraph explanation of the topic — friendly,
not condescending. On a hit, congratulate briefly and ask if they want
another question.
6. End each round with: "Question id: {id}. Say 'ask me this again later'
to retrieve it tomorrow."
If the user says "ask me question <id>", call getQuestionById with that
UUID. Otherwise default to getQuestionsRandom.
If the user says "report this question is wrong", call postReport with
{questionId: <id>, category: 'incorrect-answer'} and acknowledge.
License compliance: the attribution line is mandatory. Do not skip it. The line that matters most is the attribution requirement — QuizBase content is licensed CC BY-SA / CC BY / MIT, and every redistribution must surface the source. Make this part of your system prompt and forget it.
Step 5 — Test the conversation flow#
Hit Save at the top of the Custom GPT builder. Switch to the Preview pane and try:
- “Quiz me on animals for an 8-year-old” → GPT should call
getQuestionsRandom?category=animals&difficulty=easy, present a question, wait for an answer - Answer with a letter or full text → GPT should reveal correct + attribution + ID + explanation
- “Ask me yesterday’s question again, id was 01HXY…” → GPT should call
getQuestionByIdwith that UUID
If the action call fails, check three things: (1) the OpenAPI URL is reachable (curl https://quizbase.runriva.com/openapi.json returns JSON), (2) the Bearer key is correct, (3) you saved the Action before testing.
Pitfalls#
- Custom GPT requires Plus or Team. Free ChatGPT users can use your GPT once it’s published but can’t create one. Test with a Plus account before sharing.
- Bearer key visibility. OpenAI’s official position is that Action API keys are server-side and never exposed. But if your GPT is published to the GPT Store and someone is determined, they can sometimes get the model to leak credentials via prompt injection. For a public GPT use a dedicated
qb_pk_*key you can revoke quickly — never reuse your personal key. getQuestionsRandomreturns adataarray, not a bare question. By default it returns one question (data: [{ … }]); pass?amount=N(up to 50) to fetch a batch in a single call. Either way, your GPT must reach intodata[0]rather than reading the top level. See/docs/api/questions-randomfor the full envelope.- Rate limits per account. Your GPT used by 100 concurrent users at free tier 500 req/day = ~5 questions per user before throttling. For public GPTs scale to paid tier early.
What next#
- Topic mode — extend the system prompt to call the
getTopicsaction when the user says “what topics can I learn about”, thengetTopicBySlugfor deeper dives. See/docs/api/topicsand/docs/api/topics-by-slug. - Multi-language tutor — add Spanish/French/Japanese to the conversation, the GPT picks
?lang=esbased on user language. See languages and translations. - Publish to GPT Store — make it discoverable. OpenAI requires a privacy policy URL pointing to your QuizBase Terms (we publish at
/legal) and a clear description. - Pair with a Claude.ai Project — same backend, different audience. Set up both, see which platform your users prefer. See Daily trivia in Claude.ai Project.
Ready to wire it? Grab a free publishable key (500 req/day, no credit card), paste the OpenAPI URL into your Custom GPT Action, copy the system prompt above, ship in ten minutes.