Skip to main content
INTEGRATIONS

Integrations

The Webanto API is plain HTTP and JSON — use it from any language. Here are the fastest ways to get connected.

How an integration fits together

One rule drives the whole shape: your API key lives on your server, never in the app your users touch.

  1. Your app

    Browser, mobile, or desktop — it talks to your backend and never sees the API key.
  2. Your server

    Holds WEBANTO_API_KEY in the environment and signs every request with the Bearer header.
  3. Webanto API

    Authenticates the key, checks its scopes against the endpoint, and runs the model.
  4. Response

    JSON (or a PNG for remove-background) flows back through your server, credit cost deducted.

Call the API from your code

Server-side only — never expose your API key in a browser or client bundle. Set WEBANTO_API_KEY in your environment and go.

Python

import os
import requests

response = requests.post(
    "https://api.webanto.com/api/v1/chat",
    headers={
        "Authorization": f"Bearer {os.environ['WEBANTO_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "qwen",
        "messages": [{"role": "user", "content": "Explain embeddings in one sentence."}],
        "max_tokens": 1024,
    },
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])

JavaScript / TypeScript

const response = await fetch("https://api.webanto.com/api/v1/chat", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WEBANTO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "qwen",
    messages: [{ role: "user", content: "Explain embeddings in one sentence." }],
    max_tokens: 1024,
  }),
});

if (!response.ok) throw new Error(`Webanto API error: ${response.status}`);
const data = await response.json();
console.log(data.choices[0].message.content);

Using an AI coding assistant?

Paste the block below into Cursor, Claude Code, Copilot, or any AI coding tool. It describes the exact API contract so the assistant generates working integration code on the first try.

# Integrate with the Webanto AI API

You are integrating an app with Webanto's inference API (chat + embeddings).
It is OpenAI-shaped but NOT the OpenAI API — follow this exact contract.

## Base URL and auth
- Base URL: https://api.webanto.com/api/v1
- Get an API key at https://webanto.com/account/settings/api-keys (format: wba_live_...).
  Keys are scoped per endpoint: "chat" and/or "embeddings".
- Send it as header: Authorization: Bearer wba_live_...  (or x-api-key: wba_live_...).
- Server-side only. Never expose the key in a browser or client bundle.

## POST /api/v1/chat  (scope: chat)
Request body:
    {
      "model": "qwen",
      "messages": [{ "role": "system|user|assistant", "content": "..." }],
      "max_tokens": 1024,
      "temperature": 0.7,
      "use_memory": false,
      "reasoning_effort": "none"
    }
- model must be "qwen" (multimodal: text + vision). messages: 1-50 turns.
- qwen is a REASONING model: keep max_tokens >= ~1024 or the visible output can be empty.
- reasoning_effort is OPTIONAL and defaults to reasoning ON.
  Set "none" for extraction, classification, or structured-output work: it skips the
  hidden reasoning phase, which cuts completion tokens (and therefore credits) by a
  large factor and removes the empty-output risk above, since nothing is spent on
  reasoning. Use the default for multi-step logic, math, and planning.
  "low" | "medium" | "high" are accepted for OpenAI SDK compatibility but are NOT
  differentiated — all three mean reasoning ON.
- BILLING: credits are charged on total tokens, and reasoning tokens count toward
  completion_tokens even though the reasoning text is never returned. That is why
  reasoning_effort "none" is the single biggest cost lever on this endpoint.
- VISION: a turn's "content" is a string OR an array of parts to send an image:
      "content": [
        { "type": "text", "text": "What card is this?" },
        { "type": "image_url", "image_url": { "url": "https://cdn.example.com/card.jpg" } }
      ]
  image_url.url accepts an https:// URL or a base64 data:image/...;base64,... URL.
  Base64 must fit the ~4.5MB request-body limit — use an https URL for larger images.
Response body:
    {
      "id": "chatcmpl-...", "object": "chat.completion", "created": 0, "model": "qwen",
      "choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
      "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 },
      "credits": 1
    }

## POST /api/v1/embeddings  (scope: embeddings)
Request body: { "model": "nomic-embed-text", "input": "text" | ["t1", "t2"] }
- Models: nomic-embed-text (text), clip (text + image, shared space), dinov3 (image).
- input: up to 100 items, each <= 8192 chars.
Response body:
    {
      "model": "nomic-embed-text",
      "data": [{ "index": 0, "embedding": [0.01, -0.02] }],
      "usage": { "total_tokens": 3 },
      "credits": 1
    }

## POST /api/v1/image-embeddings  (scope: embeddings)
DINOv3 image embeddings (1280-dim, L2-normalized). Custom (non-OpenAI) contract.
Request body: one of, up to 60 images per batch (backend is CPU, ~0.3s/image):
    { "urls": ["https://cdn.example.com/card.jpg"] }
    { "images": [{ "b64": "<base64 image>" }] }
- urls must be https://. Do not mix urls and images in one request.
Response body:
    { "model": "dinov3", "dim": 1280,
      "embeddings": [[0.01, -0.02, "..."]],
      "errors": [], "credits": 1 }

## Health checks
- GET /api/v1/status  (no auth)  -> { "status": "ok", "timestamp": "..." }   (liveness)
- GET /api/v1/health  (auth)     -> { "ok": true, "organizationId": "...", "scopes": ["chat"] }

## Errors (always check the HTTP status before parsing the body)
- 400  { "error": { "code": "invalid_request | invalid_json | unknown_model", "message": "..." } }
- 401  { "error": "Missing API key" }  or  { "error": "Invalid or revoked API key" }
- 403  { "error": "API key is not scoped for \"chat\"" }
- 402  { "error": { "code": "quota_exceeded", "message": "..." }, "quota": { "used": 0, "limit": 0, "remaining": 0 } }
- 429  { "error": "...", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 30 }  plus a Retry-After header
- 502  { "error": { "code": "upstream_error", "message": "..." } }

## Reliability rules
- Every successful response returns "credits" (drawn from the organization AI pool). Budget for it.
- For safe retries, send an Idempotency-Key: <uuid> header on chat and embeddings.
- Requests are rate limited per API key; on 429, honor Retry-After and back off.

## Reference call (Node)
    const res = await fetch("https://api.webanto.com/api/v1/chat", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.WEBANTO_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": crypto.randomUUID(),
      },
      body: JSON.stringify({
        model: "qwen",
        messages: [{ role: "user", content: "Explain embeddings in one sentence." }],
      }),
    });
    if (!res.ok) throw new Error(`Webanto API ${res.status}: ${await res.text()}`);
    const { choices, credits } = await res.json();
Newsletter

Stay Ahead

Occasional, engineering-led notes on applied AI — what we're building, running, and learning in production.