Update the Devin Desktop Model Allowlist
curl --request PUT \
--url https://server.codeium.com/api/v2alpha/models \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'If-Match: <if-match>' \
--data '
{
"allowed_model_uids": [
"<string>"
],
"allowed_model_tags": [
"<string>"
]
}
'import requests
url = "https://server.codeium.com/api/v2alpha/models"
payload = {
"allowed_model_uids": ["<string>"],
"allowed_model_tags": ["<string>"]
}
headers = {
"If-Match": "<if-match>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {
'If-Match': '<if-match>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({allowed_model_uids: ['<string>'], allowed_model_tags: ['<string>']})
};
fetch('https://server.codeium.com/api/v2alpha/models', 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://server.codeium.com/api/v2alpha/models",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'allowed_model_uids' => [
'<string>'
],
'allowed_model_tags' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"If-Match: <if-match>"
],
]);
$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://server.codeium.com/api/v2alpha/models"
payload := strings.NewReader("{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("If-Match", "<if-match>")
req.Header.Add("Authorization", "Bearer <token>")
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.put("https://server.codeium.com/api/v2alpha/models")
.header("If-Match", "<if-match>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://server.codeium.com/api/v2alpha/models")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["If-Match"] = '<if-match>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_bodyAPI Reference
Update the Devin Desktop Model Allowlist
Replace your team’s Devin Desktop model allowlist programmatically: set the allowed model UIDs and tags with an ETag check against the current state.
PUT
/
api
/
v2alpha
/
models
Update the Devin Desktop Model Allowlist
curl --request PUT \
--url https://server.codeium.com/api/v2alpha/models \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'If-Match: <if-match>' \
--data '
{
"allowed_model_uids": [
"<string>"
],
"allowed_model_tags": [
"<string>"
]
}
'import requests
url = "https://server.codeium.com/api/v2alpha/models"
payload = {
"allowed_model_uids": ["<string>"],
"allowed_model_tags": ["<string>"]
}
headers = {
"If-Match": "<if-match>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {
'If-Match': '<if-match>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({allowed_model_uids: ['<string>'], allowed_model_tags: ['<string>']})
};
fetch('https://server.codeium.com/api/v2alpha/models', 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://server.codeium.com/api/v2alpha/models",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'allowed_model_uids' => [
'<string>'
],
'allowed_model_tags' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"If-Match: <if-match>"
],
]);
$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://server.codeium.com/api/v2alpha/models"
payload := strings.NewReader("{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("If-Match", "<if-match>")
req.Header.Add("Authorization", "Bearer <token>")
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.put("https://server.codeium.com/api/v2alpha/models")
.header("If-Match", "<if-match>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://server.codeium.com/api/v2alpha/models")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["If-Match"] = '<if-match>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"allowed_model_uids\": [\n \"<string>\"\n ],\n \"allowed_model_tags\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_bodyOverview
Replaces your team’s model allowlist, the same setting admins manage under organization controls in Devin Desktop. The request body is the full desired state: the two lists overwrite the stored ones. To avoid overwriting a change you have not seen, the request must carry theETag of a List Models response in If-Match. If the allowlist, the available models, or their prices changed since that response, the update is rejected with 412 and you should list the models again.
This endpoint is in alpha (
v2alpha). The request and response shape may change.Authentication
Pass an API key in theAuthorization header:
Authorization: Bearer <api-key>
cog_...). In addition to the Use Devin Desktop permission that List Models requires, the service user’s role must have the Windsurf admin permission (account.windsurf.admin), which the built-in Account Admin role includes. A service user without it can list models but gets 403 on this endpoint.
Request
string
required
The
ETag header of a GET /api/v2alpha/models response reflecting the current state. Required; * is not accepted.string[]
required
Model UIDs users may pick, e.g.
["claude-sonnet-4-5"]. Pass [] to allow models by tag only.string[]
required
Model tags users may pick, including provider tags such as
provider-anthropic or provider-openai. Pass [] to allow only the listed model UIDs.Example Request
ETAG=$(curl -s -D - -o /dev/null https://server.codeium.com/api/v2alpha/models \
-H "Authorization: Bearer $DEVIN_API_KEY" | awk 'tolower($1)=="etag:" {print $2}' | tr -d '\r')
curl -X PUT https://server.codeium.com/api/v2alpha/models \
-H "Authorization: Bearer $DEVIN_API_KEY" \
-H "If-Match: $ETAG" \
-H "Content-Type: application/json" \
-d '{"allowed_model_uids": ["claude-sonnet-4-5"], "allowed_model_tags": ["provider-anthropic"]}'
Response
On success the response is the same as List Models with the new allowlist applied: every model with itsallowed flag, the stored allowed_model_uids and allowed_model_tags, and a new ETag header.
Error Responses
Errors are returned as{"error": "<message>"}.
| Status | Cause |
|---|---|
400 | Body is not JSON, has unknown fields, or is missing allowed_model_uids or allowed_model_tags; or the filter query parameter is set (it is accepted only by List Models) |
401 | Missing or invalid API key, or a legacy Windsurf API key (only Devin cog_... service user keys are accepted) |
403 | The key’s user doesn’t have Devin Desktop access, or its role lacks the Windsurf admin permission (account.windsurf.admin) |
412 | If-Match doesn’t match the current ETag; list the models again and retry |
428 | If-Match header missing |
429 | Rate limit exceeded; wait for the Retry-After header before retrying |

