External OpenAI-compatible inference
curl --request POST \
--url https://api.compose.market/external/v1/chat/completions \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "<string>",
"messages": [
{}
],
"input": {},
"tools": [
{}
],
"stream_options": {}
}
'import requests
url = "https://api.compose.market/external/v1/chat/completions"
payload = {
"model": "<string>",
"messages": [{}],
"input": {},
"tools": [{}],
"stream_options": {}
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: '<string>', messages: [{}], input: {}, tools: [{}], stream_options: {}})
};
fetch('https://api.compose.market/external/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compose.market/external/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => '<string>',
'messages' => [
[
]
],
'input' => [
],
'tools' => [
[
]
],
'stream_options' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compose.market/external/v1/chat/completions"
payload := strings.NewReader("{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compose.market/external/v1/chat/completions")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compose.market/external/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}"
response = http.request(request)
puts response.read_bodyInference
External OpenAI-compatible inference
Use Compose models from OpenAI-compatible IDEs, agent tools, and inference wrappers.
POST
/
external
/
v1
/
chat
/
completions
External OpenAI-compatible inference
curl --request POST \
--url https://api.compose.market/external/v1/chat/completions \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "<string>",
"messages": [
{}
],
"input": {},
"tools": [
{}
],
"stream_options": {}
}
'import requests
url = "https://api.compose.market/external/v1/chat/completions"
payload = {
"model": "<string>",
"messages": [{}],
"input": {},
"tools": [{}],
"stream_options": {}
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: '<string>', messages: [{}], input: {}, tools: [{}], stream_options: {}})
};
fetch('https://api.compose.market/external/v1/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compose.market/external/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => '<string>',
'messages' => [
[
]
],
'input' => [
],
'tools' => [
[
]
],
'stream_options' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compose.market/external/v1/chat/completions"
payload := strings.NewReader("{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compose.market/external/v1/chat/completions")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compose.market/external/v1/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"<string>\",\n \"messages\": [\n {}\n ],\n \"input\": {},\n \"tools\": [\n {}\n ],\n \"stream_options\": {}\n}"
response = http.request(request)
puts response.read_bodyExternal routes are for clients that can set an OpenAI-compatible
External response bodies do not include
baseURL and bearer token but do not understand x402 challenge flows. They use Compose Key auth only.
Routes
| Method | Path | Description |
|---|---|---|
GET | /.well-known/opencode | Remote OpenCode config document. |
GET | /external/opencode | Compose provider config with env-based API key placeholder. |
GET | /external/v1/models | OpenAI-shaped model list. |
GET | /external/v1/models/{model} | One OpenAI-shaped model row. Slash-containing IDs are supported. |
POST | /external/v1/chat/completions | OpenAI-compatible chat completions. |
POST | /external/v1/responses | OpenAI-compatible Responses-style requests. |
POST | /external/v1/embeddings | OpenAI-compatible embeddings. |
POST | /external/v1/images/generations | Image generation. |
POST | /external/v1/audio/speech | Speech generation. |
POST | /external/v1/audio/transcriptions | Audio transcription. |
POST | /external/v1/videos/generations | Video generation. |
Headers
string
required
Bearer compose-.... Missing or invalid keys return an OpenAI-shaped 401.Request body
string
required
Public catalog model ID exactly as returned by
/external/v1/models.array
Chat Completions message array.
string | array
Responses or embeddings input.
array
OpenAI function tool definitions. Tool calls are returned in OpenAI shape when the provider emits them.
object
Preserved for streaming clients. Usage inclusion is forced when needed for metering.
Diagnostic headers
| Header | Description |
|---|---|
x-public-model | Public catalog ID requested by the client. |
x-upstream-provider | Provider selected by the catalog resolver. |
x-upstream-model | Provider wire model used upstream. |
compose_receipt. Upstream content-filter errors are sanitized while keeping the diagnostic headers above.⌘I