Last reviewed: November 12, 2026 (Asia/Shanghai); primary references fetched November 11 UTC.

Direct answer

For a Bruno CometAPI setup, save a POST request to the documented Chat Completions endpoint, put its base URL and model ID in an environment, and store the API key as a Bruno secret variable. Reference that secret in the Bearer Token authentication field. Add response tests that require a completed assistant text answer before treating the check as successful.

The CometAPI Chat Completions reference defines the endpoint and response fields. Bruno’s secret-variable documentation explains that environment files retain secret names while their values stay in a local encrypted store. Together, these let you share a request definition without embedding its credential.

Who this is for

This tutorial is for developers and support engineers who want a desktop API-client check before integrating CometAPI into application code. You need an installed Bruno v4 desktop app, an empty local collection, and a CometAPI credential with access to a suitable chat model.

The procedure follows current official documentation. No Bruno desktop session or authenticated CometAPI request was run for this article. The JavaScript assertions were checked separately against synthetic response fixtures; those checks do not establish desktop integration or account access.

Key takeaways

  • Keep the shared request definition separate from the local credential value.
  • Use the full operation path once; an SDK base URL alone is not the request endpoint.
  • Put only the secret reference in Bruno’s Token field; Bruno supplies the Bearer prefix.
  • Start with one non-streaming request and a harmless prompt.
  • Test response structure and completion status, rather than exact generated wording.
  • Treat every deliberate resend as another potentially billable operation.

Configure the environment and secret

Use a dedicated, empty collection named CometAPI manual check in a private local directory. This keeps unrelated collection scripts, inherited authentication, and request presets out of the initial check. Inspect any collection you reuse before giving its scripts access to a live credential.

Following Bruno’s Environment Variables guide , open the environment selector at the top right and create an environment named live-manual. Add these ordinary variables:

NameValuePurpose
COMETAPI_BASE_URLCopy the SDK base URL from the CometAPI referenceHTTPS, host api.cometapi.com, path /v1, without a trailing slash
COMETAPI_MODELgpt-6-astraModel used in the current CometAPI basic chat example

The body below follows that documented model example. Confirm its availability for your account before sending. If you select another model, review its supported endpoint and parameters before retaining the reasoning and output-limit fields.

Open this environment’s Secrets tab and add COMETAPI_KEY with your credential as its value. Save the environment and explicitly select live-manual for the request. Bruno documents the separate Secrets tab from v4.0.0; older installations can have a different interface.

Do not create the key as an ordinary environment variable. The secret-variable reference says secret values stay in Bruno’s local encrypted store and are omitted from collection-file exports. That protection depends on using the secret-variable mechanism: pasting the same value into a header, script, description, or ordinary variable creates another copy outside it.

Bruno’s environment guide documents {{varName}} interpolation and shows the selected environment in the interface. Before continuing, check the selection and look for conflicting definitions of these names in a reused collection. This workflow needs no parent environment or shared default.

The site’s repository key-handling guide provides the related checks for copied snippets and configuration files.

Build the saved chat request

Bruno’s Create a Request guide documents creating a request from a collection’s menu. Choose New Request, select HTTP, name it Chat text check, and set the method to POST. Use this URL:

{{COMETAPI_BASE_URL}}/chat/completions

With the environment above, the request uses HTTPS on host api.cometapi.com and path /v1/chat/completions. Keep /v1 in the base and /chat/completions in the request, as shown in the documented operation . Duplicating either segment sends a different path.

In Auth, select Bearer Token. Enter this in the Token field:

{{COMETAPI_KEY}}

Do not include the word Bearer in that field. The Bruno Bearer-authentication reference says Bruno constructs the Authorization header by adding the prefix. Remove any manually entered Authorization header so the request has one intended authentication configuration.

In Headers, set Content-Type to application/json. In Body, select the JSON option under RAW, as described in Bruno’s Body Data guide , and enter:

{
  "model": "{{COMETAPI_MODEL}}",
  "messages": [
    {
      "role": "user",
      "content": "Explain idempotency in one short sentence."
    }
  ],
  "reasoning_effort": "low",
  "max_completion_tokens": 2048,
  "stream": false
}

The CometAPI reference documents these fields for its example model. The 2,048-token ceiling is an illustrative starting choice. It includes reasoning and visible output, so it does not promise 2,048 visible tokens or completion of every prompt. Keep model-specific sampling settings out of this baseline.

Save the request. Keep collection, folder, and request hooks free of extra HTTP calls or chaining for this first check. Use the individual request’s Send action after completing the review below; running a whole collection can execute additional requests.

Add tests for completed text

Open the request’s Tests tab and paste this script. Bruno’s Testing guide documents test, Chai expect assertions, and the response methods used here.

test("HTTP 200 with JSON media type", function () {
  expect(res.getStatus()).to.equal(200);
  const mediaType = String(res.getHeader("content-type") || "")
    .split(";")[0].trim().toLowerCase();
  expect(mediaType).to.equal("application/json");
});

test("one completed assistant text answer", function () {
  expect(res.getStatus()).to.equal(200);
  const body = res.getBody();
  expect(body).to.be.an("object");
  expect(body.object).to.equal("chat.completion");
  expect(body.choices).to.be.an("array").with.lengthOf(1);

  const choice = body.choices[0];
  expect(choice).to.be.an("object");
  expect(choice.finish_reason).to.equal("stop");
  expect(choice.message).to.be.an("object");
  expect(choice.message.role).to.equal("assistant");
  expect(choice.message.content).to.be.a("string");
  expect(choice.message.content.trim().length).to.be.above(0);
  expect(choice.message.refusal == null).to.equal(true);
  expect(choice.message.tool_calls == null).to.equal(true);
});

These are the acceptance rules for this text-only check. They require exactly one choice, assistant text, a stop finish reason, and an absent or null refusal and tool-call field. Other valid API outcomes need different handling. A length-limited response or a tool call should fail this check even when the server returned HTTP 200.

The script deliberately avoids asserting that the answer equals a particular sentence. Model wording can vary while the response still satisfies the intended structure. It also leaves room for additional response properties instead of demanding an exact set of object keys.

Tests evaluate the returned response; they do not prevent the request from being sent or impose a financial limit. A failed assertion can follow a billable generation. Keep test output private until reviewed, since assertion diagnostics can include response values.

Contract details to verify

Before clicking Send, review the selected environment, resolved destination, secret reference, JSON body, and empty script hooks. Send the individual request once, then inspect its response and test results.

BoundaryRequired observationWhat to do if it differs
DestinationHTTPS on api.cometapi.com, path /v1/chat/completionsCorrect the base or operation path before sending
CredentialToken field references COMETAPI_KEY; no pasted key or duplicate Authorization entryRepair local configuration without sharing the resolved header
ModelThe reviewed model ID and its supported parameters are selectedRecheck the model-specific request contract
HTTP responseStatus 200 with JSON media typeClassify the HTTP or response-format failure
CompletionBoth tests pass, with one completed text choiceInspect finish reason, refusal, and content shape
Usage evidenceInspect the returned usage fields when presentRecord missing evidence explicitly; do not infer zero usage

Retain the observation time, installed Bruno version, request name, selected model, HTTP status, assertion results, and completion category. Record usage values only as observed. Avoid copying the credential, resolved request headers, prompt text, or full response into a shared troubleshooting note.

For the assertion script’s local fixture check, a synthetic completed response passed both tests. Separate fixtures with HTTP 401, HTML media type, malformed completion structure, empty choices, a length finish reason, blank text, a refusal, or tool calls each caused at least one test to fail. This verifies the assertions’ behavior under those supplied inputs. It does not reproduce Bruno’s transport, interpolation, or response parsing.

Before sharing the collection, inspect its saved files and an export for literal credentials and private text. A secret-variable export omits the secret value; it does not sanitize a value pasted elsewhere. The recipient must provision their own local secret and verify the active environment.

Failure modes

Authentication fails. Check the selected environment, the locally populated secret, and the Token field. A saved secret name without its local value is incomplete configuration. Do not paste a resolved Authorization header into a support ticket.

A request-shape problem looks like a server outage. Compare the method, path, and body with the CometAPI reference. Its documented 500 examples include a missing messages field, so the status alone cannot identify the cause. Inspect any error body privately before deciding to resend.

HTTP 200 appears successful, but the text check fails. Review object, choices, finish_reason, and the assistant message. Do not turn a refusal or truncated result into accepted text by removing assertions simply to obtain green indicators.

The response is an event stream. Confirm stream is the Boolean false and inspect any script that modifies the body. This script expects a complete JSON completion; SSE needs separate event handling and tests.

The request times out. Preserve the observation before another Send. The remote operation may have progressed even though the desktop client lacks a completed response. This procedure adds no retry loop, and the response assertions establish no cancellation or duplicate-suppression guarantee.

A collection works only on its author’s machine. Check the recipient’s local secret provisioning, environment selection, installed version, and model access. Sharing the file does not transfer the encrypted local credential value.

Sources checked

Seven primary public references returned HTTP 200 on November 11, 2026 UTC, corresponding to November 12 in Asia/Shanghai:

These maintained technical references support an evergreen integration task. Bruno’s current documentation describes v4; compare the steps with your installed release. The narrow text acceptance rule and manual review sequence are recommendations built from the documented interfaces.

FAQ

Can I share the collection without sharing my key?

Bruno documents that secret-variable values remain local and are omitted from collection-file exports. Review the other saved fields and files too, then have each recipient populate their own secret. Ordinary variables and literal headers do not gain that protection automatically.

Do passing tests prove the generated answer is correct?

They establish the structural conditions encoded in this script for the observed response. Review the answer’s factual quality separately. The tests also do not establish pricing, account-wide availability, or performance under load.

Should this collection run automatically in CI?

Keep this credential-backed manual check separate from routine offline tests. For a Node.js application, the site’s Undici MockAgent tutorial provides a different workflow for testing request construction and failures with synthetic responses.

Reader next step

Save one request, review its active environment and authentication reference, and perform one small manual check. Keep the actual test results and usage observations with your local setup notes, then inspect the collection files before sharing them.

Need a credential for that first account-backed request? Start with CometAPI , then return to the same saved request and verification steps.