DEV Community

Cover image for Why we built one API for AI research, media, and editable artifacts
Jathan Spaulding
Jathan Spaulding

Posted on AI-assisted

Why we built one API for AI research, media, and editable artifacts

A useful AI product often needs more than a text completion. It may need current research with sources, file understanding, generated media, an editable PowerPoint or spreadsheet, durable job state, storage, and usage accounting.

Building each of those paths against a different provider creates a familiar problem: every integration has its own authentication, retry rules, status model, output format, and billing data. The first demo can be quick. The production system is not.

That is the problem we built the 3Stone API to address: one server-side API contract for research, creation, and finished artifacts.

One key, synchronous and asynchronous work

Simple chat can complete synchronously:

const response = await fetch("https://one.3stoneai.com/v1/chat", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THREESTONE_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "3stone-auto",
    input: "Explain the tradeoffs between queues and scheduled polling.",
  }),
});

const result = await response.json();
Enter fullscreen mode Exit fullscreen mode

Creation endpoints use durable jobs. The initial request returns a job ID; clients poll that job and download the artifact only after it reaches a completed state.

const headers = {
  Authorization: `Bearer ${process.env.THREESTONE_API_KEY}`,
  "Content-Type": "application/json",
};

const submitted = await fetch(
  "https://one.3stoneai.com/v1/presentations",
  {
    method: "POST",
    headers: {
      ...headers,
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      prompt: "Create a concise editable project update deck.",
    }),
  },
).then((response) => response.json());

let job;
do {
  await new Promise((resolve) => setTimeout(resolve, 1500));
  job = await fetch(
    `https://one.3stoneai.com/v1/jobs/${submitted.job_id}`,
    { headers },
  ).then((response) => response.json());
} while (["queued", "provider_starting", "running"].includes(job.status));

if (job.status !== "completed") {
  throw new Error(`Job stopped: ${job.error?.code}`);
}

const artifact = await fetch(
  `https://one.3stoneai.com/v1/jobs/${submitted.job_id}/artifact`,
  { headers },
);
Enter fullscreen mode Exit fullscreen mode

The same job pattern applies to spreadsheets, images, and video.

Why idempotency is part of the API contract

Long-running provider work can outlive an HTTP connection. A blind retry can create duplicate provider cost or duplicate customer-visible output. Every mutating request therefore accepts a stable idempotency key.

If the same key is reused with a different body, the API returns an idempotency conflict. If an accepted provider operation cannot yet be authoritatively reconciled, the request can enter a reconciliation-required state. Clients should preserve the request and job IDs instead of replaying the work.

That distinction matters: a network timeout does not prove that external execution failed.

Research should return sources

A research response is only useful when the application can retain and render its evidence:

import json, os, urllib.request, uuid

request = urllib.request.Request(
    "https://one.3stoneai.com/v1/research",
    data=json.dumps({
        "query": "Research current battery recycling policy and cite primary sources."
    }).encode(),
    headers={
        "Authorization": f"Bearer {os.environ['THREESTONE_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
        "Content-Type": "application/json",
    },
    method="POST",
)

with urllib.request.urlopen(request, timeout=120) as response:
    result = json.load(response)
    print(result["output_text"])
    for source in result.get("sources", []):
        print(source["url"])
Enter fullscreen mode Exit fullscreen mode

Finished artifacts, not screenshots

The current public release includes:

  • AI/chat
  • source-backed research
  • file and image understanding
  • image and video generation
  • editable PowerPoint, Excel, and document files
  • music
  • interactive HTML tools

We deliberately do not advertise public API website/software execution or webhooks yet. Production truth matters more than a long launch list.

Keep the key on the server

Never expose a 3Stone API key in browser or mobile code. Put calls behind your authenticated backend or server function. Persist the returned request and job IDs so reconnecting clients can resume without duplicating work.

Useful production error handling includes:

  • 401 invalid_api_key: reject and rotate/revoke as appropriate.
  • 402 insufficient_balance: fund the usage balance before new provider work.
  • 409 idempotency_conflict: use a new key only when the request body truly changes.
  • 429 rate_limit_exceeded: back off with jitter.
  • reconciliation_required: preserve identifiers and do not replay blindly.

What we want feedback on

3Stone API is usage-based and separate from consumer subscriptions. Developer Mode exposes keys, balance, request logs, charges, and job state.

I would especially value feedback on:

  1. the durable async-job contract;
  2. artifact lifecycle and download ergonomics;
  3. which end-to-end examples would make evaluation easiest;
  4. which capability combinations are hardest to maintain across separate providers.

Quickstart, OpenAPI, and examples: https://github.com/jathanks3/3stone-developer-apis?utm_source=devto&utm_medium=content&utm_campaign=api_launch

Documentation: https://www.3stoneai.com/developers/docs?utm_source=devto&utm_medium=content&utm_campaign=api_launch

Developer Mode: https://one.3stoneai.com/developer?utm_source=devto&utm_medium=content&utm_campaign=api_launch

Disclosure: this article was prepared with AI assistance and reviewed by the 3Stone founder.

Top comments (1)