Shift Note: Sending a Model Request with curl
The first shell request needs four pieces
Calling a model API from a shell script means sending an HTTP request and reading the response. You do not need a full application or an SDK to start. The curl command handles the network connection, while your script supplies an address, an API key, a model identifier, and a prompt.
This is useful for small operational tasks: summarizing a saved incident note, drafting a release summary, or testing whether an integration is configured correctly. Start with harmless sample text rather than customer records, credentials, or internal logs.
Set the destination and credentials before sending
Relay365 is a pay-as-you-go relay desk for multiple model families. It can fit a shell workflow when you want a common API entry point rather than separate connection settings for each provider. Its API base is https://api.api365relay.com; OpenAI-compatible clients use that base with /v1 appended.
The example below uses the OpenAI-compatible chat completions route. Confirm that your selected model supports that route before running it. A display name such as Gemini 2.5 Flash is not necessarily the exact identifier accepted in a request: copy the API model identifier from the service's current model listing.
You need curl and jq installed. The latter builds and reads JSON without fragile shell quoting. In a local Bash session, read your key without echoing it:
read -r -s -p 'Relay365 API key: ' RELAY365_API_KEY; printf '\n'; export RELAY365_API_KEY
Then set the model identifier and a small test prompt:
export MODEL_ID='paste-the-supported-model-id-here'; export PROMPT='Summarize this shift note in one sentence: The deployment finished, and the team is checking application logs.'
Do not paste real keys into scripts or commit them to version control. For scheduled jobs, inject the key through your automation platform's secret store. Avoid shell tracing with set -x around authenticated requests, because it can expose expanded secrets.
Build valid JSON instead of hand-escaping prompts
A request body describes the model and the conversation. Here, one user message contains the task. Use jq to create the file:
jq -n --arg model "$MODEL_ID" --arg prompt "$PROMPT" '{model: $model, messages: [{role: "user", content: $prompt}]}' > request.json
The --arg options safely encode quotes, newlines, and other characters inside your prompt. Simply inserting shell variables into handwritten JSON can break as soon as the input contains quotation marks. Treat request.json as potentially sensitive if you later replace the sample with real operational data.
Send one request and save the response
Run this as one shell command:
curl --silent --show-error --fail-with-body --connect-timeout 10 --max-time 120 --dump-header response.headers --output response.json --write-out '%{http_code}\n' 'https://api.api365relay.com/v1/chat/completions' --header "Authorization: Bearer $RELAY365_API_KEY" --header 'Content-Type: application/json' --data-binary @request.json
The two timeout values are local limits you choose, not service guarantees. The command saves response headers and the body separately, and prints the HTTP status. The --fail-with-body option makes HTTP errors produce a nonzero exit status while retaining the error body; older curl installations may need upgrading to support it.
After a successful response, extract the usual chat completion text:
jq -r '.choices[0].message.content' response.json
This assumes a standard text response. If the result is null or unexpected, inspect the saved JSON rather than assuming the model returned an empty answer.
Check failures before putting the script on a schedule
- Check curl's exit status immediately, and stop the script when the request fails.
- For authentication errors, verify the key and permissions without printing the secret.
- For an unknown model or route error, check the exact model identifier and endpoint compatibility.
- For rate limits or temporary failures, use bounded retries with delays, and honor Retry-After when supplied.
- Remember that a timeout does not prove the server stopped processing; retrying may duplicate work and cost.
Keep the first automation small and observable
If a shared, pay-as-you-go entry point suits your workflow, create a Relay365 account and test one supported model with a short prompt. Before scheduling anything, record timestamps and status codes, restrict access to saved files, and review usage. A small script is easier to trust when you can tell what it sent, whether it succeeded, and when to stop it.
Further desk bulletins
| Date | Bulletin title | Filed under | Language |
|---|---|---|---|
| 2026-10-29 | Desk Log: First OpenAI-Compatible Call in Ten Minutes | Wire & endpoints | EN |
| 2026-10-29 | Duty Desk Note: Privacy Checks Before Relay Traffic | Wire & endpoints | EN |
| 2026-10-29 | Shift Note: Wiring Slack Mentions to a Model API | Wire & endpoints | EN |
| 2026-10-29 | Shift note: relay handles transit, not model behavior | Wire & endpoints | EN |
| 2026-10-28 | Shift note: Node.js model streams reaching browsers | Wire & endpoints | EN |
| 2026-10-28 | Shift Note: Relay Requests In, Model Answers Out | Wire & endpoints | EN |
Curious how it runs on your own code? Sign up →