curl --request POST \
--url https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"marketId": "PM-WC26-WIN-FRA-WC26-1910V2",
"indexSet": "14",
"amount": "1000000"
}
'import requests
url = "https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature"
payload = {
"marketId": "PM-WC26-WIN-FRA-WC26-1910V2",
"indexSet": "14",
"amount": "1000000"
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({marketId: 'PM-WC26-WIN-FRA-WC26-1910V2', indexSet: '14', amount: '1000000'})
};
fetch('https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature', 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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature",
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([
'marketId' => 'PM-WC26-WIN-FRA-WC26-1910V2',
'indexSet' => '14',
'amount' => '1000000'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature"
payload := strings.NewReader("{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}"
response = http.request(request)
puts response.read_body{
"signedOpId": "<string>",
"digest": "<string>",
"backendSig": "<string>",
"salt": "<string>",
"deadline": 123,
"marketId": "<string>",
"indexSet": "<string>",
"amount": "<string>",
"vaultAddress": "<string>",
"tokenIds": [
"89348190...",
"15823942...",
"60912834..."
]
}Co-sign a NegRisk position convert (NO-basket → USDC)
NegRisk-only collapse of a NO-basket back to collateral. Routes through NegRiskAdapter on-chain rather than the vault CTF path.
curl --request POST \
--url https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: <api-key>' \
--data '
{
"marketId": "PM-WC26-WIN-FRA-WC26-1910V2",
"indexSet": "14",
"amount": "1000000"
}
'import requests
url = "https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature"
payload = {
"marketId": "PM-WC26-WIN-FRA-WC26-1910V2",
"indexSet": "14",
"amount": "1000000"
}
headers = {
"X-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({marketId: 'PM-WC26-WIN-FRA-WC26-1910V2', indexSet: '14', amount: '1000000'})
};
fetch('https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature', 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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature",
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([
'marketId' => 'PM-WC26-WIN-FRA-WC26-1910V2',
'indexSet' => '14',
'amount' => '1000000'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: <api-key>"
],
]);
$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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature"
payload := strings.NewReader("{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Api-Key", "<api-key>")
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://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature")
.header("X-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://core.api.dev.predictstreet.sde.adifoundation.ai/api/vault/convert-signature")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"marketId\": \"PM-WC26-WIN-FRA-WC26-1910V2\",\n \"indexSet\": \"14\",\n \"amount\": \"1000000\"\n}"
response = http.request(request)
puts response.read_body{
"signedOpId": "<string>",
"digest": "<string>",
"backendSig": "<string>",
"salt": "<string>",
"deadline": 123,
"marketId": "<string>",
"indexSet": "<string>",
"amount": "<string>",
"vaultAddress": "<string>",
"tokenIds": [
"89348190...",
"15823942...",
"60912834..."
]
}Authorizations
Partner / integrator key — format ps_live_<keyId>_<secret>. Partner keys are issued by PredictStreet; a user can also create keys bound to their own wallet from their account settings in the web app. Never ship to a browser. multi_wallet partners must additionally send X-User-Wallet: 0x<40-hex> on every authenticated request to declare the acting wallet. See the API keys guide for scope taxonomy, partner kinds, rate limits, and rotation procedure.
Headers
Optional. Requests carrying the same key replay the original operation instead of creating a new one. When omitted, the key is derived from the wallet, the operation and the exact request body, so a byte-identical repeat replays the first operation — send your own key to run the same operation twice.
Body
Off-chain market symbol, e.g. "PM-WC26-WIN-FRA-WC26-1910V2". The server resolves it to the on-chain bytes32 NegRisk marketId; only neg-risk markets resolve, and a binary market fails with market_not_found.
"PM-WC26-WIN-FRA-WC26-1910V2"
uint256 indexSet bitmap over outcomes to collapse, decimal string. Each bit set marks an outcome whose NO position is being burned. Server converts to bigint at the EIP-712 boundary. Example: a 4-outcome market collapsing NO_1 + NO_2 + NO_3 → indexSet = 0b1110 = 14. Empty (0) and full-universe (every outcome) sets are rejected.
"14"
uint256 base-units (decimal string) — burned per outcome bit set in indexSet. NOT human-decimal: pass raw uint256 since CTF outcome tokens already mint 1:1 against 6-decimal USDC. Empty / zero rejected.
"1000000"
Response
Operation id (UUID) for this convert co-sign — pass it to POST /api/me/positions/op-submit.
EIP-712 digest the user must sign with their EOA private key. The returned backendSig is over this exact digest; both signatures are submitted on-chain to vault.convertPositions(...).
0x-hex backend EIP-712 signature over digest.
uint256 EIP-712 salt (decimal string).
uint256 EIP-712 deadline (Unix seconds).
bytes32 NegRisk on-chain marketId — the value the contract expects in convertPositions(marketId, ...). Distinct from the off-chain marketSymbol in the request: this is the resolved on-chain identifier.
uint256 indexSet bitmap (decimal string), echoed back.
uint256 base units echoed back.
Per-user vault address (EIP-712 verifyingContract).
Ordered list (ascending outcome index) of decimal-string tokenIds the convert will BURN. Lets clients preview/display the operation without re-deriving the indexSet → tokenId mapping.
["89348190...", "15823942...", "60912834..."]