curl --request POST \
--url https://vouchersx.com/integrations/users \
--header 'Content-Type: application/json' \
--header 'x-partner-slug: <x-partner-slug>' \
--header 'x-signature: <api-key>' \
--data '
{
"externalUserId": "<string>",
"email": "jsmith@example.com",
"displayName": "<string>",
"phone": "<string>",
"countryCode": "<string>",
"locale": "<string>"
}
'import requests
url = "https://vouchersx.com/integrations/users"
payload = {
"externalUserId": "<string>",
"email": "jsmith@example.com",
"displayName": "<string>",
"phone": "<string>",
"countryCode": "<string>",
"locale": "<string>"
}
headers = {
"x-partner-slug": "<x-partner-slug>",
"x-signature": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-partner-slug': '<x-partner-slug>',
'x-signature': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
externalUserId: '<string>',
email: 'jsmith@example.com',
displayName: '<string>',
phone: '<string>',
countryCode: '<string>',
locale: '<string>'
})
};
fetch('https://vouchersx.com/integrations/users', 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://vouchersx.com/integrations/users",
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([
'externalUserId' => '<string>',
'email' => 'jsmith@example.com',
'displayName' => '<string>',
'phone' => '<string>',
'countryCode' => '<string>',
'locale' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-partner-slug: <x-partner-slug>",
"x-signature: <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://vouchersx.com/integrations/users"
payload := strings.NewReader("{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-partner-slug", "<x-partner-slug>")
req.Header.Add("x-signature", "<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://vouchersx.com/integrations/users")
.header("x-partner-slug", "<x-partner-slug>")
.header("x-signature", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://vouchersx.com/integrations/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-partner-slug"] = '<x-partner-slug>'
request["x-signature"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"userId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created": false
}{
"userId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created": true
}{
"error": "validation_failed"
}{
"error": "invalid_signature"
}{
"error": "partner_disabled"
}{
"error": "upsert_failed"
}Upsert a partner user
Idempotently provisions or updates a consumer from the partner backend. Keyed by (partner_id, externalUserId). If the user was previously anonymised via DELETE, calling this again revives the row and clears anonymizedAt. PII fields that are omitted are left unchanged; passing an explicit null clears them.
curl --request POST \
--url https://vouchersx.com/integrations/users \
--header 'Content-Type: application/json' \
--header 'x-partner-slug: <x-partner-slug>' \
--header 'x-signature: <api-key>' \
--data '
{
"externalUserId": "<string>",
"email": "jsmith@example.com",
"displayName": "<string>",
"phone": "<string>",
"countryCode": "<string>",
"locale": "<string>"
}
'import requests
url = "https://vouchersx.com/integrations/users"
payload = {
"externalUserId": "<string>",
"email": "jsmith@example.com",
"displayName": "<string>",
"phone": "<string>",
"countryCode": "<string>",
"locale": "<string>"
}
headers = {
"x-partner-slug": "<x-partner-slug>",
"x-signature": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-partner-slug': '<x-partner-slug>',
'x-signature': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
externalUserId: '<string>',
email: 'jsmith@example.com',
displayName: '<string>',
phone: '<string>',
countryCode: '<string>',
locale: '<string>'
})
};
fetch('https://vouchersx.com/integrations/users', 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://vouchersx.com/integrations/users",
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([
'externalUserId' => '<string>',
'email' => 'jsmith@example.com',
'displayName' => '<string>',
'phone' => '<string>',
'countryCode' => '<string>',
'locale' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-partner-slug: <x-partner-slug>",
"x-signature: <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://vouchersx.com/integrations/users"
payload := strings.NewReader("{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-partner-slug", "<x-partner-slug>")
req.Header.Add("x-signature", "<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://vouchersx.com/integrations/users")
.header("x-partner-slug", "<x-partner-slug>")
.header("x-signature", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://vouchersx.com/integrations/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-partner-slug"] = '<x-partner-slug>'
request["x-signature"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalUserId\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"displayName\": \"<string>\",\n \"phone\": \"<string>\",\n \"countryCode\": \"<string>\",\n \"locale\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"userId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created": false
}{
"userId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created": true
}{
"error": "validation_failed"
}{
"error": "invalid_signature"
}{
"error": "partner_disabled"
}{
"error": "upsert_failed"
}Authorizations
Stripe-style HMAC signature over ${timestamp}.${rawBody} using your partner secret. Format: t=<unix-seconds>,v1=<hex-hmac-sha256>. Also requires the x-partner-slug header to identify your account.
Headers
Your partner slug, assigned at onboarding (e.g. acme).
Body
Your stable identifier for this user (typically your DB primary key). Treated as opaque — we never decode it. Used as the JWT sub claim and in every outbound webhook payload to link your records to ours.
1 - 255User email. Pass null to clear; omit to leave unchanged.
320Shown in our admin views and (absent a JWT override) in the webview.
200E.164 recommended but not enforced. Used only for support lookups.
40ISO 3166-1 alpha-2. Auto-uppercased server-side.
2BCP-47 locale tag (e.g. en-SG). Currently informational.
16