Skip to content

Run an operation in the user's browser

OpenAPI JSON
POST
/call
Code sample: Python
import os, requests
r = requests.post(
"https://api.eu.apigami.com/call",
headers={
"Authorization": f"Bearer {os.environ['APIGAMI_API_KEY']}",
"Accept": "application/octet-stream",
"Accept-Encoding": "gzip",
},
json={
"user_ref": "usr_3f9c1a0e7b2d4c58a61e9f0b2d7c4e13",
"op": "request.fetch",
"params": {"domain": "example.com", "url": "https://api.example.com/me", "method": "GET", "responseType": "binary"},
},
)
if not r.ok:
raise RuntimeError(r.json()["error"])
print(int(r.headers["X-Apigami-Status"]), r.text)
# Requests decodes gzip automatically. Use r.content for file bytes.

Runs one operation in a browser of the user named by user_ref and returns its result. Apigami takes the browser install_ref names, or else the user’s most recently active browser that can reach the site, and keeps it for the whole call. An action calling with its run token names neither: the run’s browser runs it. For request.fetch, request gzip by default with all three settings: outer Accept: application/octet-stream, outer Accept-Encoding: gzip, and params.responseType: “binary”. This also applies to text or JSON from the site. These HTTP headers do not belong in params.headers. Read the raw response body, X-Apigami-Status for the target status and X-Apigami-Headers for its JSON-encoded headers. Enable gzip decoding in your HTTP client, such as curl –compressed; Node fetch and Python Requests decode automatically. Gzip requires install 0.4.0+; small bodies, already compressed types and older installs may return uncompressed bytes. The default JSON envelope and other operations do not use this compression. The first call for a domain the user was never asked about opens the approval window in their browser and waits up to 45 s for the answer: approved, the call goes through in the same request; refused, 403 no grant; unanswered, 403 with consentPending. A call runs for at most 20 s in the browser. emails.send is answered by Apigami itself, never by a browser: no domain, no approval window, and the email reaches the user whether or not a browser of theirs is online. ai.generate is answered by Apigami too, in the EU: a model reads the instructions, text, PDFs and images you send and answers in text, or in JSON following output_schema; it takes no user_ref, and a page’s token is refused.

Accept-Encoding
string
Example
gzip

Send gzip together with Accept: application/octet-stream and params.responseType: “binary” to request compression of request.fetch responses. This header alone is insufficient. Enable automatic gzip decoding in your HTTP client.

Media typeapplication/json
object
user_ref

With an application key, required, except for ai.generate, which refuses it, and emails.send to the team: the user whose browser runs the call, as POST /connect/token returned it. Under a run token (an action calling POST /call with the token Apigami handed it in X-Apigami-Run), leave it out: the run already names its browser, and naming one answers 400.

string
<= 200 characters
install_ref

With an application key, optional: one browser of that user, as POST /connect/token or GET /v1/users/{user_ref}/installs returned it. Named, that browser is never replaced by another; left out, Apigami takes the most recently active browser of the user that can reach the site. Under a run token, leave it out: the run already names its browser, and naming one answers 400.

string | null
<= 200 characters
op
required
string
Allowed values: request.fetch tabs.list tabs.open tabs.navigate tabs.parse tabs.read tabs.storage tabs.click tabs.type tabs.screenshot tabs.scroll tabs.settle tabs.select tabs.key session.getCookies session.refresh stream.subscribe stream.unsubscribe stream.list emails.send ai.generate
params
required
One of:
request.fetch
object
domain
required

A bare host name, e.g. example.com. The site the user approved; subdomains are covered.

string
url
required

Https:// only, on the domain or one of its subdomains.

string format: uri
method
string
default: GET
headers

Cookie, Origin, Referer, User-Agent and Host are set by the browser and cannot be overridden.

object
key
additional properties
string
headersFromCookies

Map header names to cookie names. Values are read and attached inside the browser without being returned to your app.

object
key
additional properties
string
body

Sent as-is. Encode JSON yourself and set content-type.

string
bodyEncoding

How Apigami rebuilds the bytes of body, not the format the target expects: the target only ever receives bytes and never sees base64, and what it expects is what content-type says. text sends the UTF-8 of the string; base64 decodes it to bytes first, which is how you PUT a PDF, a DOCX or an image to an upload url. A target that wants JSON carrying a base64 field takes that JSON as a plain text body instead. Requires install 0.8.0 or later: an older one answers 409 rather than send the base64 itself.

string
default: text
Allowed values: text base64
responseType

Use binary with outer Accept: application/octet-stream and Accept-Encoding: gzip to request raw responses with compression, even for text or JSON. text decodes UTF-8; base64 returns a base64 body in the JSON envelope. binary without the raw Accept header also returns a base64 envelope. Do not set compress: Apigami decides from the install version.

string
default: text
Allowed values: text base64 binary
appId

Optional: the key already identifies the app. Must be the key’s app.

string format: uuid
clientId

Optional, for older integrations. Same as appId.

string format: uuid
Examples

Fetch raw bytes; send Accept: application/octet-stream and Accept-Encoding: gzip

{
"user_ref": "usr_3f9c1a0e7b2d4c58a61e9f0b2d7c4e13",
"op": "request.fetch",
"params": {
"domain": "example.com",
"url": "https://api.example.com/me",
"method": "GET",
"headers": {
"accept": "application/json"
},
"responseType": "binary"
}
}

The operation ran. With responseType binary and Accept: application/octet-stream, request.fetch returns raw bytes and target metadata in X-Apigami-* headers. Otherwise result has the shape of the operation. The target may return a non-2xx status even though this response is 200; Apigami failures use non-2xx JSON responses.

string format: binary
X-Apigami-Status
integer
Example
200

Raw mode only: the target HTTP status.

X-Apigami-Headers
string
Example
{"content-type":"application/json"}

Raw mode only: target headers encoded as a JSON object.

Content-Encoding
string
Allowed values: gzip

Raw mode only, present when compression was applied. Decode once; clients with automatic decompression already return decoded bytes.

X-Apigami-Body-Downgraded
string
Allowed values: base64

Raw mode only: an install older than 0.3.0 required conversion. The response still contains raw bytes.

Malformed request: user_ref missing, op or params.domain missing, or an appId / clientId that is not the key’s app. For emails.send, the first field of the email refused, by its path, the rendered email over 100 kB included. For ai.generate, a user_ref or install_ref sent, the first parameter refused by its path, or an input the model cannot read.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples
{
"error": "user_ref is required"
}

Missing, malformed or revoked API key.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Example
{
"error": "bad client credentials"
}

Not allowed: the team has no active member, the user is not connected to this app (never connected on the connect page, or disconnected), Apigami disabled their account, they left the team of your private app, install_ref is not one of this user’s browsers, the user refused this domain, or has not answered yet (consentPending). For emails.send: the manifest declares no emails permission, sending is suspended after complaints, news to a user who did not tick your news box, the address cannot receive, or a run token that serves nobody. For ai.generate: the version whose code calls declares no ai permission, a page’s token, or a team the AI is not open to yet.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples
{
"error": "no grant"
}

The user_ref is not one of your team’s.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Example
{
"error": "unknown user_ref"
}

No browser of the user can take the call: none is attached, none holds the browser’s permission for the site, or several are online for a site never asked about and none is named. For ai.generate under an API key: your application has no production version yet.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples
{
"error": "no browser is attached to this user"
}

Ai.generate: more than 14 MB of files, decoded, in one call.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Example
{
"error": "ai input too large: at most 14 MB of files per call"
}

Ai.generate: a file that is not a PDF, plain text or a PNG, JPEG or WebP image. Convert a Word or OpenDocument file to PDF or text first.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Example
{
"error": "unsupported media type application/msword: send pdf, text or an image"
}

Ai.generate: the model ran and gave nothing usable. Its filters declined the content (do not retry as is), or a structured answer was cut at max_output_tokens.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples
{
"error": "the model declined to answer: safety"
}

Emails.send: that user already received 5 emails in the last 24 hours, from every application together, or, for kind: marketing, your news in the last 24 hours (one a day per application and per user). Nothing was sent.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples

5 emails in 24 hours

{
"error": "emails rate limited"
}

The operation failed in the browser, or its answer was lost: the browser dropped mid-call, or did not answer within 35 s. A lost answer carries outcome: "unknown": the request may have reached the site, so check before repeating a write. For emails.send, the email could not be sent, try again later: nothing was sent. For ai.generate, the model could not answer, try again later.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Example
{
"error": "operation has timed out"
}

That browser cannot be reached. Nothing is queued. extension offline: the browser holds no connection right now. extension outdated: Apigami in that browser must be updated before it can connect again; Chrome installs the update on its own, and restarting Chrome applies it sooner. For ai.generate: the model is busy, with Retry-After in seconds, or the AI is not configured on Apigami’s side.

Media typeapplication/json
object
error
required
string
consentPending

Present and true when the approval window is open and the user has not answered within 45 s.

boolean
Examples
{
"error": "extension offline"
}
Retry-After
integer
Example
60

Ai.generate, busy model only: the seconds to wait before trying again.