One endpoint for language-model completions, with per-connection credentials, quotas and logging. Inference runs on our own hardware; nothing you send is passed to a third-party model provider.
You are issued a client secret for each connection. It looks like brg_… and is shown to your administrator once, at issuance — we store only a hash of it and cannot show it again. Keep it as you would a database password.
You do not send the client secret on API calls. You exchange it for a short-lived access token, and send the token:
curl -s https://bridge.mynexuss.com/v1/token \
-H 'content-type: application/json' \
-d '{"client_secret":"brg_YOUR_SECRET"}'
{"access_token":"eyJ…","token_type":"Bearer","expires_in":900}Present it on every request as Authorization: Bearer <access_token>, and exchange again when it expires. Tokens are signed with ES256; the public keys are at /.well-known/jwks.json if you want to verify one yourself.
Everything the API does is POST https://bridge.mynexuss.com/v1/chat/completions. It is wire-compatible with the OpenAI chat-completions shape, so most existing client libraries work by changing the base URL and the key.
curl -s https://bridge.mynexuss.com/v1/chat/completions \
-H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{
"model": "Qwen3.8-27B-FP8",
"messages": [
{"role":"system","content":"You answer concisely."},
{"role":"user","content":"Summarise the attached contract."}
],
"max_tokens": 800,
"temperature": 0
}'| Field | What it does |
|---|---|
model | Required. Must be a model your connection is permitted to call — GET /v1/models lists them. |
messages | Required. The whole conversation, oldest first. The API is stateless: we do not remember previous calls, so send the history you want considered. |
max_tokens | Ceiling on the reply. Clamped to your connection's per-request ceiling. |
temperature | 0 to 2. Use 0 for deterministic output; only 0 is eligible for caching. |
top_p | Nucleus sampling, above 0 and at most 1. |
stop | Up to four stop sequences. |
stream | true for server-sent events. Deltas arrive as data: lines; the last carries usage, then data: [DONE]. |
cache | false to bypass the response cache for this call. There is no opt-in — caching is enabled per connection by your administrator. |
Two fields are refused with an error rather than quietly ignored, because an SDK that sends them and is ignored produces wrong output in production:
tools / function calling — a callable tool takes caller-supplied parameters and is therefore an injection surface. Exposing one needs its own design, not a request field.response_format — structured output is not implemented yet. Accepting it and returning unconstrained prose would be worse than refusing it.Small attachments can ride on the request itself as content parts:
{"role":"user","content":[
{"type":"text","text":"Summarise this."},
{"type":"input_file","filename":"contract.pdf","file_data":"<base64>"}
]}
Past your connection's inline limit — by count or by total size — the request is
refused with 413 inline_file_limit_exceeded. That is
deliberate: dropping the fourth attachment silently would give you an answer that looks
complete and is not. Upload the files first, then reference them.
curl -s https://bridge.mynexuss.com/v1/files \
-H "authorization: Bearer $TOKEN" \
-F 'file=@contract.pdf'
{"id":"file_9tK...","object":"file","filename":"contract.pdf","bytes":184320,"expires_at":1760000000,"scanned":true}
{"role":"user","content":[
{"type":"text","text":"Summarise this."},
{"type":"input_file","file_id":"file_9tK..."}
]}
Every upload is virus-scanned before it is stored. Text-bearing formats (PDF, Word,
PowerPoint, Excel, CSV, RTF, ODT, HTML, plain text and Markdown) have their text
extracted and given to the model; images are sent as pictures.
File contents are never returned — GET /v1/files/{id} gives
you the details and nothing else. This is not a storage service.
The same rule as a conversation: one hour from the last time you used it, and never more than 48 hours in total. Referencing a file in a prompt counts as using it, so a document you keep coming back to during a long piece of work stays available the whole time.
A file being used by a live conversation is kept until that conversation finishes, even if the file's own hour has run out. That is what stops a document disappearing from under a discussion you are still having.
curl -s -X DELETE https://bridge.mynexuss.com/v1/files/file_9tK... \
-H "authorization: Bearer $TOKEN"
You will get one of two answers, and the difference matters if you are acting on someone's request to erase their data:
{"id":"file_9tK...","deleted":true,"status":"deleted",
"detail":"The file's content has been deleted."}
The content is gone.
{"id":"file_9tK...","deleted":false,"status":"pending","removed_at":1760003600,
"detail":"The file is still being used by a conversation, ..."}
The file was still in use by a live conversation, so the content has not been
deleted yet. It can no longer be referenced by new requests, and it will be
removed when that conversation stops being active — removed_at tells you
when that is expected. We will not tell you something is deleted while it still
exists, so check deleted rather than assuming a 200 means gone. If
you have a webhook set up, we will tell you when it finally goes.
Every answer comes back with an id. Send it as
previous_response_id on your next request and the model picks up where you
left off — it has the earlier questions and its own earlier answers in front of it.
That is what makes a series of refinements a conversation rather than a set of
unrelated calls.
{"id":"resp_7cQ…","object":"chat.completion","choices":[…]}
{
"model": "Qwen3.8-27B-FP8",
"previous_response_id": "resp_7cQ…",
"messages": [{"role":"user","content":"Shorter, and drop the second clause."}]
}
You send only the NEW message. Do not resend the history — we already have it, and resending it would be charged to you twice.
One hour from the last time you used it, not from when it was created. So a back-and-forth you are actively working on stays alive as long as you keep going, and one you walk away from is deleted an hour later. There is also an outer limit — a thread is removed after 48 hours whatever happens.
When a thread has expired, or if you send an id we do not recognise, the
request still succeeds — it is simply answered as a fresh conversation. Check
the continues field on the response if you need to know which happened: it
carries the id you supplied when we picked the thread up, and is absent when we did
not. That way a timed-out thread degrades into a slightly worse answer rather than an
error in the middle of someone's workflow.
While a thread is live we hold the turns in it — your questions and our answers —
because that is what the next turn is built from. That is a separate thing from the
logging described below, and it is bounded: the thread is deleted when it expires, and
deleted immediately if your connection is cut off. Send "store": false on
a request to keep that particular exchange out of it entirely; you then get no id back
and cannot follow up on it.
If your administrator has turned follow-ups off for your connection, nothing is held between requests at all and every question is answered on its own.
| Limit | What happens |
|---|---|
| Daily token quota | 429 quota_exhausted with Retry-After. Resets at 00:00 UTC. It is a hard stop, not a slowdown. |
| Requests per minute | 429 rate_limited with Retry-After. |
| Output tokens per request | Your max_tokens is clamped to the connection ceiling. |
| Request timeout | 504 timeout. |
| Address restriction | If your connection is restricted to particular addresses, a request from anywhere else is refused. Ask your administrator which addresses are registered. |
Every response carries an x-request-id. Quote it when asking about a specific call — it is how we find the record without you having to tell us what you sent.
{"error":{"message":"…","type":"invalid_request_error","code":"model_not_allowed","request_id":"…"}}
Branch on code, which is stable; message is for a human. The codes are: unauthenticated, connection_not_usable, source_address_not_allowed, operation_not_allowed, model_not_allowed, quota_exhausted, rate_limited, invalid_request, inline_file_limit_exceeded, file_not_found, file_refused, unsupported, context_window_exceeded, upstream_unavailable, timeout, internal_error.
Your administrator can give us an address to notify when something changes that your
software would otherwise have to guess at. We send a POST with a small JSON
body — never any of your content — for these:
| Event | Means |
|---|---|
conversation.closed | A conversation has finished. Its reference can no longer be followed up. |
file.deleted | A file's content has been removed — it expired, or the connection was closed. |
file.deletion_completed | A file you asked us to delete, which was still in use at the time, has now been removed. |
quota.exhausted | Your allowance for the day has run out. Sent once a day, not once per refused request. |
connection.revoked | Your access has been withdrawn. This is the last thing you will hear from us. |
POST /your-endpoint
Bridge-Signature: t=1789240000,v1=4a7f...
content-type: application/json
{"id":"0193...","type":"conversation.closed","created_at":1789240000,
"connection_id":"22ff...","subject":"resp_7cQ...","reason":"expired"}
Every delivery is signed with a key your administrator gives you. Take the
t= value and the exact body you received, join them with a full stop, and
compute an HMAC-SHA256 using your key. It should equal the v1= value:
signed = t + "." + raw_body
expected = hex( hmac_sha256(your_signing_key, signed) )
Compare the two in constant time. Reject anything where t is more
than a few minutes old — the timestamp is part of what is signed precisely so
that an old delivery cannot be replayed at you later.
We retry six times with increasing gaps — roughly 30 seconds, then 1, 2, 4 and 8 minutes — and then stop and record the failure for your administrator to see. Nothing is queued indefinitely. Deliveries are sent in the background, so a slow or unavailable endpoint of yours never slows down your API requests.
We only send to https addresses on the public internet, and we do not follow redirects. If your address stops resolving publicly we stop delivering to it.
This is worth reading rather than assuming, because it is not what most APIs do.
We record that a request happened, how many input tokens it used, how many output tokens it produced, and how long it took — along with which connection made it, which model, and the outcome.
We do not store your prompts or the model's replies. Not truncated, not redacted, not for a short period: they are not written down. Upstream error messages are reduced to one of our own codes before anything is recorded, because a model's error can quote your prompt back.
There is one exception and it is deliberate. If you ask us to investigate a specific problem, an administrator can switch content capture on for your connection for a bounded period, with a stated reason and an audit record. It is off by default, it expires by itself, and it is visible to your administrator the whole time it is on.
The follow-up threads described above are the other place your words are held, and they are held for a different reason and for a bounded time — an hour from last use, with a 48-hour outer limit, deleted on expiry and deleted immediately if your connection is cut off. A thread can only ever be continued by the connection it was issued to.