DEV Community

Cover image for Send your first AI message in one API call
Jonathan Murray for Backboard.io

Posted on

Send your first AI message in one API call

Built-in memory and model routing

Most AI tutorials start with a setup checklist. Pick a model provider. Create an account. Wire up a vector database for memory. Stand up a server to hold conversation state. Glue it all together. Then, finally, you send a message.

Backboard skips all of that. One API call sends your first message. A thread, an assistant, memory, and routing across thousands of models are already running behind that single call. You do not assemble the stack. It is the stack.

Here is the whole thing.

Step 1: Get a key

Sign up at app.backboard.io, go to Settings then API Keys, and copy your key. New accounts get $5 in free credits for 30 days. No credit card.

That is the only setup. Keep your key server-side, never in frontend or mobile code.

Step 2: Send the message

Pick your language. Same call in all three.

Python

pip install backboard-sdk
Enter fullscreen mode Exit fullscreen mode
import asyncio
from backboard import BackboardClient

async def main():
    client = BackboardClient(api_key="YOUR_API_KEY")

    response = await client.send_message(
        "Hello! Tell me a fun fact about space."
    )

    print("Reply:", response.content)
    print("Thread ID:", response.thread_id)
    print("Assistant ID:", response.assistant_id)

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

JavaScript (Node 18+)

No install needed. Just fetch.

const response = await fetch("https://app.backboard.io/api/threads/messages", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Hello! Tell me a fun fact about space.",
  }),
});

const result = await response.json();

console.log("Reply:", result.content);
console.log("Thread ID:", result.thread_id);
console.log("Assistant ID:", result.assistant_id);
Enter fullscreen mode Exit fullscreen mode

cURL

curl -X POST "https://app.backboard.io/api/threads/messages" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello! Tell me a fun fact about space."}'
Enter fullscreen mode Exit fullscreen mode

Run it. You get a reply. That is your first AI message.

What just happened

You sent one string. Backboard did the rest:

  • Created a thread. The thread_id in the response is a live conversation. Send the next message with it and the model remembers what was said.
  • Created an assistant. The assistant_id is a reusable profile. Attach memory, documents, and tools to it later without changing your call.
  • Picked a model. No provider config required. It defaulted to openai / gpt-4o. You can change that with two parameters, shown below.

No vector DB. No state server. No provider SDK. One call.

Continue the conversation

Pass the thread_id back. The model now has context.

follow_up = await client.send_message(
    "Make it shorter.",
    thread_id=response.thread_id,
)
print(follow_up.content)  # knows you mean the space fact
Enter fullscreen mode Exit fullscreen mode

That is stateful conversation with zero extra infrastructure.

Swap the model with two parameters

One key gives you thousands of models. Change the provider and model per message. Same thread, same code.

response = await client.send_message(
    "Explain quantum computing simply.",
    llm_provider="anthropic",
    model_name="claude-sonnet-4-20250514",
)
Enter fullscreen mode Exit fullscreen mode

Want a different model next turn? Change two strings. You are never locked to one provider.

Turn on memory

Add memory="Auto" and the assistant remembers facts across conversations, not just within one thread.

# Thread 1: tell it something
await client.send_message(
    "My name is Sarah and I prefer dark mode.",
    assistant_id="your-assistant-id",
    memory="Auto",
)

# Thread 2, same assistant: it remembers
reply = await client.send_message(
    "What do you remember about me?",
    assistant_id="your-assistant-id",
    memory="Auto",
)
print(reply.content)  # "Your name is Sarah and you prefer dark mode."
Enter fullscreen mode Exit fullscreen mode

Persistent memory, one parameter. No database to provision.

The point

The first call is one line because the platform is full-stack. Memory, model routing, RAG, and stateful threads sit behind a single key. You start with a working AI message, then turn on capabilities as you need them by adding parameters, not services.

Sign up, grab a key, and send your first message: app.backboard.io

Full docs: docs.backboard.io

Top comments (9)

Collapse
 
dannwaneri profile image
Daniel Nwaneri

Took you up on the suggestion from the Standard Model thread — went and read the docs and the API properly.
The one-call DX is genuinely clean. The fact that thread state, model routing, and memory are provisioned automatically behind a single endpoint removes the scaffolding problem that kills most AI tutorials before anyone gets to the interesting part. That's real value.

The limitation I was curious about after our exchange: the write-side. memory="Auto" handles persistence, but the question from the Standard Model thread is about what gets written and how causality gets encoded — not just whether the memory call succeeds. The sequencing problem the article describes (failure at T1, resolution at T2, causal link only visible in retrospect) lives at the ingestion layer, not the retrieval layer. Managed memory handles the second problem well. The first one requires hooks the managed layer probably doesn't expose by design.

That's not a criticism of the product. The abstraction is the point. It's just where the trade-off lives for anyone coming from a production agent architecture who wants to control the write path. For the audience this tutorial is aimed at, none of that matters yet. For the audience reading the Standard Model thread, it's the question

Collapse
 
jacksoul_c3a27b9c8184 profile image
江欢(JackSoul)

The single-call onboarding is very approachable. Once the abstraction hides provider/model setup, the next question teams usually ask is “how do I see and control spend by assistant/thread/user?”

Do you expose usage metadata or budget controls per thread/assistant/provider? That kind of attribution becomes important when the platform is choosing or routing models behind one simple API.

Collapse
 
jon_at_backboardio profile image
Jonathan Murray Backboard.io

Good question, and the right one to ask the moment the abstraction hides routing.

Yes on both, and it's a full dashboard, not raw metadata you assemble yourself. You get spend and token economics per model and per provider (deepseek on openrouter, opus on bedrock, gpt on openai, side by side), a spend breakdown across token usage, vector reads, and memory writes, an input versus output split, subscription versus usage credits, biggest-usage days, and a filterable per-event log down to the individual call and its cost.

On control: you set pre-pay and usage limits, and budget tracks per assistant. The part I like most is what happens at the cap. Instead of erroring out, the assistant falls back to open-source free models until you add budget, so spend is bounded but the app keeps running. Building for multiple companies? Split it into separate organizations and attribution stays isolated per project.

Native grouping is by model and provider. To slice by thread or user, you pivot on the per-call metadata: every response carries model_provider, model_name, and total_tokens, and you hold thread_id and assistant_id, so those axes roll up cleanly.

That's the point of routing behind one API. You should be able to see and cap the spend, not just inherit it.

Collapse
 
jacksoul_c3a27b9c8184 profile image
江欢(JackSoul)

Thanks, this is exactly the level of detail I was hoping for. The fallback-at-cap behavior is a nice product decision too — bounded spend without turning a user-facing app into a hard failure path.

The per-call log + assistant/org boundaries answer most of the attribution question. For thread/user rollups, it sounds like the main requirement is keeping your own IDs consistently attached on the app side, then joining against the response metadata. That feels like the right trade-off for a one-call abstraction: dashboard for the common views, metadata for product-specific accounting.

Collapse
 
nir_doshi_a3e435aa9543105 profile image
nir doshi

This is Extremely easy to understand!!

Collapse
 
benjamin_nguyen_8ca6ff360 profile image
Benjamin Nguyen • Edited

Omg! You guys are dev.to. I am so happy to see an Ottawa company here :). I follow you guys on my LinkedIn account.

Collapse
 
kenielzep97 profile image
Self-Correcting Systems

Daniel's write-side observation is the right frame. The abstraction this article is
selling — one call, managed memory, no infrastructure — is genuinely valuable for the
audience it's aimed at. But the trade-off he's naming is real: managed memory handles
the retrieval layer cleanly and leaves the ingestion layer mostly opaque.

The failure mode I've seen documented in production agent work is specific: the item
that gets written carries a metadata claim about its own authority — what it governs,
what actions it permits — and the system later reads that claim and acts on it. If the
claim was wrong at write-time (wrong resource class, wrong scope), retrieval works
perfectly, the execution gate reads a clean record, and the action fires on bad
information. The system never errors. It just does the wrong thing with confidence.

A managed write path doesn't solve that. It actually makes it harder to inspect,
because the ingestion hook isn't exposed by design. For anyone building agents where
the write-path needs to carry verified authority metadata — not just content — that's
where the abstraction runs out. Not a criticism of this product. Just where the
boundary is.

Collapse
 
uzoma_uche_3ec83974b4a8a5 profile image
Echo

Step-by-step is underrated. Most 'first API call' articles skip over the 401 / rate-limit edge case that actually costs the first hour. Walking through it saves people from a 'why is this not working' tail.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.