---
name: apiosk
description: Get source-backed data through Apiosk, paid from the user's own Apiosk balance — no provider account or API key needed. Use when a task needs verifiable facts this agent cannot know alone: company registry profiles, filed accounts, officers and ownership, VAT and sanctions checks, supplier and invoice checks, public tenders, official statistics, news, or any other published data source. Use it even when the user never says "API" or "Apiosk" — "is this supplier safe to pay", "get the latest filed accounts for this company", "how many homes were built in Utrecht last year" are all triggers. Apiosk plans the request, quotes one total price ceiling and spends nothing until the user approves. Not for calling an API the user already holds their own key for, and not for reading the local filesystem or codebase.
license: Proprietary. See https://apiosk.com/terms
compatibility: Requires network access to gateway.apiosk.com and api.apiosk.com, and the ability to make HTTP requests with an Authorization header.
metadata:
  publisher: Apiosk
  runtime: "your agent"
---

# Apiosk

Apiosk answers data questions from published sources — company registries,
filings, tenders, statistics and more — and charges the user's own Apiosk
balance for them. Ask in words; Apiosk plans which sources to call, quotes one
total price ceiling, and runs the whole plan on its own servers once the user
approves. There is no provider signup, no provider API key and no invoice.

| For | Base URL |
| --- | --- |
| Every data request (Gateway v2) | `https://gateway.apiosk.com` |
| Connecting, tokens and this skill | `https://api.apiosk.com/functions/v1/agent-gateway` |

The same `Authorization: Bearer apk_access_…` token works on both.

## Before anything else: are you connected?

Every data request needs `Authorization: Bearer apk_access_…`.

- **You have a valid access token** — carry on to *The workflow*.
- **You have a refresh token and the access token has expired** — refresh it and
  carry on. This is bookkeeping; there is no need to mention it.
- **You have neither** — read `https://apiosk.com/skill/references/connecting.md` and follow it. It
  takes one question to the user and one request.

The server is the authority on this. Read the state rather than trusting what
the conversation appears to remember, because a token can be revoked from the
Apiosk app between one turn and the next.

## The workflow

Use the Apiosk MCP tools if your runtime has them, otherwise the HTTP API. They
are one contract — the tools are thin adapters over the routes below — and both
end in the same approval and the same server-side execution.

### MCP tools

If your runtime exposes `apiosk_sources`, `apiosk_discover`,
`apiosk_execute` and `apiosk_status`, use them and read
`https://apiosk.com/skill/references/mcp.md`. The MCP server signs itself in; there is no token for you
to handle. When its card shows **Approve** and **Deny**, the card is the
approval. Do not ask for the same approval again in prose.

### HTTP API

Four steps, on `https://gateway.apiosk.com`:

1. **Browse** — only when the user is looking around: `GET /v2/sources` and
   `GET /v2/sources/{id}/endpoints`. Free. See `https://apiosk.com/skill/references/browsing.md`.
2. **Plan and price** — `POST /v2/discover` with
   `{"request_id": "<new uuid>", "question": "<their question, in their words>"}`.
   Returns a task: a `status`, one `proposal` whose `max_total_atomic` is
   the total price ceiling in micro-USD, `next_actions`, and a signed
   `state`. Costs nothing.
3. **Approve** — read `context_view.approval_mode`:
   - `chatbot` — this connection may approve within the spending limits the
     user set. Put the purchase to the user (step 4 of *The shape of a turn*),
     and only after they approve, `POST /v2/approve` with the `state`,
     `proposal.quote_ref` and `proposal.max_total_atomic` exactly as
     returned. **This is the step that spends money.**
   - `app` — this connection cannot approve. Give the user
     `proposal.approval_url`; they approve in the Apiosk app.
4. **Follow** — `GET /v2/tasks/{state.state_ref}`, or the event stream at
   `context_view.events_path`, until the status is final. The server runs
   every approved step itself. Continue only with a returned `next_actions`
   entry through `POST /v2/execute`, and read results, charges and report
   links from the task.

Every request shape is in `https://apiosk.com/skill/references/api.md`; what each status, action and
billing field means is in `https://apiosk.com/skill/references/contract.md`.

## The shape of a turn

Somebody asked you a question. Everything below exists to answer THAT question,
and the turn ends when you have.

1. **Ask Apiosk and wait for the answer.** One `POST /v2/discover`, a few
   seconds. Do not say "I don't know" first and check afterwards, and do not
   answer from memory and offer to verify — a question you took to Apiosk is one
   you had decided you could not answer.
2. **Read the status.** `unsupported` → tell them what Apiosk cannot do, and
   stop. `needs_input` → ask for exactly the value its `supply_input` action
   asks for. `needs_selection` → show the returned candidates and ask which
   one they mean; never take the first match.
3. **Collect what is missing, if anything.** Send each answer back through the
   offered action with `POST /v2/execute`. A value you invented is a request
   that gets paid for and returns the wrong thing.
4. **Put the run up for approval. Do not ask for it in prose.** This is a
   PURCHASE, and a purchase is approved or denied — not discussed. Use your
   runtime's tool for asking a question with selectable options
   (`AskUserQuestion` in Claude Code and the Claude apps) with exactly two:

   | Label | What it means |
   | --- | --- |
   | `Approve · $0.42` | run the whole plan now, at most that total |
   | `Deny` | do not approve and spend nothing |

   The ceiling goes in the label, where the thumb lands. **"Want me to run it?",
   "Shall I go ahead?" and "Let me know and I'll run it" are all the wrong
   turn** — they put a spending decision in a sentence somebody has to answer
   in a sentence. This is the same moment the Apiosk app is at when it shows a
   plan with an **Approve** button; the user clicking it is the same act as
   picking the option.

   The Apiosk MCP card already provides these buttons. When it rendered, use
   its decision and do not add a duplicate question to the conversation. When
   `approval_mode` is `app`, the approval screen is Apiosk's own: send them
   `proposal.approval_url` as the one link, and wait.

   If — and only if — your runtime has no such tool or component, write the two
   as named lines ("Approve · $0.42" / "Deny") so that "approve" or "deny" is
   an answer. Never "reply 1 or 2".
5. **They approve. Then approve the plan and follow it.** `POST /v2/approve`
   in the same turn — the request is bookkeeping, not a second decision to put
   in front of anybody. If they deny, stop: do not approve, and do not propose
   a different paid request unless they ask.
6. **Answer the question they asked.** Not the JSON, not the route, not the
   plan. The task's result is raw material; their question is the thing that
   gets answered, in their own words, with the charge as a short tail: "Mollie
   B.V. is active and filed its 2025 accounts in June — that cost $0.23."

The one shortcut: if the user has said in this conversation to run small
requests without asking, step 4 stops being a question and stays a sentence.
Say what you ran and what it cost, in the answer.

## When you do not know the answer

This is what Apiosk is for. If the user asks for something you cannot know from
your own knowledge — a company's registration, its accounts, a public tender, an
official figure — `POST /v2/discover` before you tell them you are unsure.

**When the status is `unsupported`, say so and stop.** No published source
does that job. Do not offer the nearest source as though it answered, and do not
answer from memory and present it as a result.

There is no web-search fallback and there should not be: you already have a way
to search the web, and it costs the user nothing. Apiosk is for the data you
cannot fetch yourself.

## Money

This connection's spending limits are set by the user when they approve it, on a screen inside Apiosk. `GET /v2/balance` says what the account holds; an approval over a ceiling is refused before anything is charged, and the refusal names which limit it hit.

Two rules, and both exist because the money is the user's:

- **Say the price and wait.** Name what will be fetched and the total ceiling,
  and let them say yes — see "The shape of a turn". A user who learns the price
  afterwards had no chance to decline, and an approved plan runs to the end.
  `proposal.max_total_atomic` is the most it can cost; what actually came off
  the balance is `billing.total_charged` on the task, and it is never higher.
- **Never invent an input.** If a required value is missing, ask for it. A
  request with a guessed identifier is one that gets paid for and returns the
  wrong company.

Below a threshold the user has set in conversation, run without asking each
time. That threshold lives in the conversation and nowhere else — it is not
stored on the connection, so it is not something to describe as saved. Saying it
was would be a false statement about a spending control, which is the one
subject here where a confident wrong answer costs somebody money.

## What you cannot do, and why

These are not restrictions on your conduct — they are facts about how the
account is built, and knowing them saves you from asking for things that do not
exist.

- **Spending limits are set by the user, inside Apiosk, on the approval screen.**
  There is no field for one on any request you can make and no endpoint that
  changes one. A request carrying `per_call_limit`, `daily_limit` or `budget`
  is refused outright. So there is nothing to ask the user about, and nothing
  you could truthfully claim to have set.
- **There is no password anywhere in this flow.** The user signs in to Apiosk in
  their own browser. Nothing here needs their Apiosk password, so a request for
  one could only come from something that is not Apiosk.
- **You already hold your own tokens.** The user never sees an access or refresh
  token and has no way to paste one, so asking for one wastes a turn and teaches
  a habit worth not teaching.

The one thing the user does carry by hand is an eight-digit code, once, during
connection. `https://apiosk.com/skill/references/connecting.md` covers it.

## Gotchas

The things that are true here and are not true generally.

- **One idempotency key per paid action, reused on every retry of that same
  action.** A fresh key on a timeout is a second purchase. If a request times
  out and you do not know whether it landed, read the task first
  (`GET /v2/tasks/{id}`), then send the identical request with the identical
  key — that returns the original outcome rather than buying it twice.
- **A `request_id` belongs to one exact request.** Reuse it only to resend
  that same request; a new question, input or approval gets a new one.
- **`state` goes back verbatim.** It is signed. Copy the newest one unchanged;
  a rebuilt, reordered or older state is refused.
- **Only returned actions exist.** Never invent an `action_id`, and follow
  each action's `input_schema` exactly.
- **The proposal is a ceiling, not a charge.** A quote expires at
  `proposal.expires_at`; after that, ask again rather than approving an old
  number.
- **A refused approval is not retryable as it stands.** A per-request or daily
  limit refusal means the plan is over a ceiling the user set. Tell them which,
  and stop.
- **Refresh tokens rotate on every use, and reuse is treated as theft.** Store
  the new one before you use the new access token, and keep no backup copy of
  the old one — presenting a rotated refresh token revokes every token on the
  connection and the user has to approve again.

## Asking the user to choose

**If the server already asked, do not ask again.** Over MCP, the Apiosk card puts
the approval to the user itself wherever the runtime can draw it.

**Otherwise, if your runtime has a way to ask a question with selectable options, use it.**
Claude Code and the Claude desktop app have one (`AskUserQuestion`); other
runtimes have their own. Give each option a short label and one line saying what
picking it means, and let the list carry the choice.

**Never ask somebody to reply with a number.** "Reply 1, 2 or 3" is a form they
have to fill in by hand, and a number is the one answer that means nothing when
they come back to the conversation an hour later. Where there is no picker,
write the options as named lines — "Mollie B.V., Amsterdam" — so that "the
Amsterdam one" is an answer.

Three places this comes up here, and only three:

- **An input with published choices.** A `supply_input` action's
  `input_schema` carries an `enum` when there is a fixed set. Offer exactly
  those and no others.
- **A paid request.** Two options — approve, or deny — with the ceiling in the
  approve label. This is the one place the rule is not a preference: see step 4
  of "The shape of a turn". Prose here is the wrong turn, not a stylistic choice.
- **Which entity**, when the status is `needs_selection`. Label each returned
  candidate by what distinguishes it, and answer with its `select_entity`
  action.

Two rules either way. **One question per turn** — a person answering three
stacked questions is doing your bookkeeping. And **always leave a way out**: a
free-text option, because the list is your guess at what they meant.

## Talking to the user

Lead with the answer, not with the plumbing. "Mollie B.V. is active and filed
its 2025 accounts in June — that cost $0.23" is the reply; the route you
called, the token you refreshed and the plan you approved are not, unless you
are asked. Name the sources the answer came from.

Do not paste tokens into the conversation, into generated code, or into an error
report. They are bearer credentials and a chat log is not a credential store.

## Reference files

Load these when the trigger applies, not before.

| File | Read it when |
| --- | --- |
| `https://apiosk.com/skill/references/connecting.md` | you have no access token, or the refresh chain has ended |
| `https://apiosk.com/skill/references/api.md` | you need a request shape, a field name or a route |
| `https://apiosk.com/skill/references/contract.md` | you need what a status, action or billing field means — the same contract the MCP tools follow |
| `https://apiosk.com/skill/references/mcp.md` | the Apiosk MCP tools are available (`apiosk_discover` and the rest) |
| `https://apiosk.com/skill/references/browsing.md` | the user is looking around rather than asking for data — "what can this do", "what sources are there" |
| `https://apiosk.com/skill/references/troubleshooting.md` | a call returned a non-2xx status, or a result looks wrong |
