curl --request POST \
--url https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"weights": "default"
}
'import requests
url = "https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions"
payload = { "weights": "default" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({weights: 'default'})
};
fetch('https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions', 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.moonbounce.ai/v1/decisions/{decision_id}/suggestions",
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([
'weights' => 'default'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.moonbounce.ai/v1/decisions/{decision_id}/suggestions"
payload := strings.NewReader("{\n \"weights\": \"default\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.post("https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"weights\": \"default\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"weights\": \"default\"\n}"
response = http.request(request)
puts response.read_body{
"environment": "production",
"suggestion": {
"id": "<string>",
"decision_id": "<string>",
"kind": "feature_rewrite",
"target": "structure",
"summary": "<string>",
"environment": "production",
"base": {
"structure": "<string>",
"weights": "<string>",
"engine_profile": "<string>"
},
"candidate": {
"structure": "<string>",
"weights": "<string>",
"engine_profile": "<string>",
"weights_pin": "default"
},
"requires": [
"<string>"
],
"candidate_spec": {
"format": "moonbounce.spec/0",
"nodes": {},
"output": "<string>",
"guidance": "<string>",
"target": "<string>",
"meta": {
"title": "<string>",
"description": "<string>",
"owner": "<string>",
"tags": [
"<string>"
],
"display": {}
}
},
"diff": [
{
"op": "replace",
"path": "<string>",
"before": "<unknown>",
"after": "<unknown>"
}
],
"evidence": {
"label": "starting_examples",
"frozen_at": "2023-11-07T05:31:56Z",
"by_split": {
"seed": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"train": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"holdout": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
}
},
"noise_rate": 0.5,
"action_mix": {
"before": {},
"after": {},
"on": "<string>",
"n": 1
},
"changed_items": [
{
"evaluation_id": "<string>",
"feedback_id": "<string>",
"split": "seed",
"change": "fix",
"before": {
"features": {},
"outcome": "<string>",
"score": 123
},
"after": {
"features": {},
"outcome": "<string>",
"score": 123
}
}
],
"credited_feedback": [
"<string>"
],
"by_source": {
"app_native": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"app_resolution": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"reviewer": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"owner": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"import": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"synthetic_review": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
}
}
},
"preview_id": "<string>",
"generator": {
"name": "<string>",
"version": "<string>",
"model": "<string>"
},
"status": "proposed",
"created_at": "2023-11-07T05:31:56Z",
"status_at": "2023-11-07T05:31:56Z",
"rejection_reason": "<string>",
"accepted_evaluation_id": "<string>",
"accepted_at": "2023-11-07T05:31:56Z",
"stale": {
"reason": "<string>",
"cases": 2
}
},
"reason": "<string>"
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}Generate a suggestion on demand
Proposes one change to the decision’s spec from its feedback (a feature rewrite or a new feature), with its preview as evidence. suggestion is null when there is nothing worth proposing, and reason says why. The same candidate proposed earlier in the key’s environment is returned instead of a duplicate. Everything is read in the key’s environment: its feedback, cases and suggestions (a test key learns from test cases only). Without spec, the base is the version serving primary traffic in that environment (else the latest version seeded there). A spec sent here must already be registered (evaluated, seeded or POST /v1/decisions//structures), or it is 404 VERSION_UNKNOWN. Send outcomes unless the base’s latest primary evaluation in the key’s environment, else its latest seed there, recorded cut points (400 OUTCOMES_INVALID). Only cases with an outcome from the owner, reviewers or seeds count. 503 ENGINE_UNAVAILABLE (retry later) when no generator is configured, the generator’s provider fails, or the engines could not score the cases.
curl --request POST \
--url https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"weights": "default"
}
'import requests
url = "https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions"
payload = { "weights": "default" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({weights: 'default'})
};
fetch('https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions', 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.moonbounce.ai/v1/decisions/{decision_id}/suggestions",
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([
'weights' => 'default'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.moonbounce.ai/v1/decisions/{decision_id}/suggestions"
payload := strings.NewReader("{\n \"weights\": \"default\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.post("https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"weights\": \"default\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.moonbounce.ai/v1/decisions/{decision_id}/suggestions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"weights\": \"default\"\n}"
response = http.request(request)
puts response.read_body{
"environment": "production",
"suggestion": {
"id": "<string>",
"decision_id": "<string>",
"kind": "feature_rewrite",
"target": "structure",
"summary": "<string>",
"environment": "production",
"base": {
"structure": "<string>",
"weights": "<string>",
"engine_profile": "<string>"
},
"candidate": {
"structure": "<string>",
"weights": "<string>",
"engine_profile": "<string>",
"weights_pin": "default"
},
"requires": [
"<string>"
],
"candidate_spec": {
"format": "moonbounce.spec/0",
"nodes": {},
"output": "<string>",
"guidance": "<string>",
"target": "<string>",
"meta": {
"title": "<string>",
"description": "<string>",
"owner": "<string>",
"tags": [
"<string>"
],
"display": {}
}
},
"diff": [
{
"op": "replace",
"path": "<string>",
"before": "<unknown>",
"after": "<unknown>"
}
],
"evidence": {
"label": "starting_examples",
"frozen_at": "2023-11-07T05:31:56Z",
"by_split": {
"seed": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"train": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"holdout": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
}
},
"noise_rate": 0.5,
"action_mix": {
"before": {},
"after": {},
"on": "<string>",
"n": 1
},
"changed_items": [
{
"evaluation_id": "<string>",
"feedback_id": "<string>",
"split": "seed",
"change": "fix",
"before": {
"features": {},
"outcome": "<string>",
"score": 123
},
"after": {
"features": {},
"outcome": "<string>",
"score": 123
}
}
],
"credited_feedback": [
"<string>"
],
"by_source": {
"app_native": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"app_resolution": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"reviewer": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"owner": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"import": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
},
"synthetic_review": {
"n": 1,
"fixes": 1,
"breaks": 1,
"base_correct": 1,
"candidate_correct": 1,
"holdout_uses": 1,
"owner_decided": 1
}
}
},
"preview_id": "<string>",
"generator": {
"name": "<string>",
"version": "<string>",
"model": "<string>"
},
"status": "proposed",
"created_at": "2023-11-07T05:31:56Z",
"status_at": "2023-11-07T05:31:56Z",
"rejection_reason": "<string>",
"accepted_evaluation_id": "<string>",
"accepted_at": "2023-11-07T05:31:56Z",
"stale": {
"reason": "<string>",
"cases": 2
}
},
"reason": "<string>"
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"reason": "<string>",
"param": "<string>",
"details": {}
}
}Authorizations
An API key, sent as Authorization: Bearer <key>. Each operation lists the scope its key needs: decisions:evaluate (evaluate and read evaluations), decisions:feedback (your app's feedback) or decisions:develop (everything else, including owner feedback and the review queue). An operation listing no scope takes any scoped key. A key with no scope, or without the operation's, gets 403 INSUFFICIENT_SCOPE.
Headers
Makes a retry safe. For 24 hours, the same key with the same request returns the first stored result (with Idempotent-Replayed: true); the same key with a different request is a 422 IDEMPOTENCY_KEY_REUSED. Keys are scoped to the organization and environment across every operation, so use a fresh key per request. A request that failed before its result was stored runs again when retried with its key.
1 - 255^[\x20-\x7e]+$Path Parameters
^[a-z][a-z0-9_.-]{0,127}$Body
When given, generate this kind even if blame points to the other; the replay gate still decides whether it is proposed.
feature_rewrite, new_feature Show child attributes
Show child attributes
default Show child attributes
Show child attributes
Response
The suggestion, or why there is none
The environment of the key that made the call: production or test. Evaluations, feedback, reviews and suggestions are separate per environment; registered structures are shared.
production, test Show child attributes
Show child attributes