curl --request PUT \
--url https://proxy.primer.io/{proxyPath} \
--header 'Content-Type: application/json' \
--header 'X-Primer-Api-Key: <api-key>' \
--header 'X-Target-Host: <x-target-host>'import requests
url = "https://proxy.primer.io/{proxyPath}"
headers = {
"X-Target-Host": "<x-target-host>",
"X-Primer-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {
'X-Target-Host': '<x-target-host>',
'X-Primer-Api-Key': '<api-key>',
'Content-Type': 'application/json'
}
};
fetch('https://proxy.primer.io/{proxyPath}', 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://proxy.primer.io/{proxyPath}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Primer-Api-Key: <api-key>",
"X-Target-Host: <x-target-host>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://proxy.primer.io/{proxyPath}"
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Add("X-Target-Host", "<x-target-host>")
req.Header.Add("X-Primer-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.put("https://proxy.primer.io/{proxyPath}")
.header("X-Target-Host", "<x-target-host>")
.header("X-Primer-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.asString();require 'uri'
require 'net/http'
url = URI("https://proxy.primer.io/{proxyPath}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-Target-Host"] = '<x-target-host>'
request["X-Primer-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
response = http.request(request)
puts response.read_body{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"forwarded": "false",
"token": "<string>",
"field": "<string>"
}Forward a request to a PSP, substituting vaulted card data
Forwards the request to the host named in X-Target-Host, at the given path,
substituting any card placeholders in the body first.
Any additional headers on the request, including the merchant’s own PSP
credential (for example Authorization), are passed through unchanged.
X-Primer-Api-Key authenticates you against Primer.
A placeholder has the form {{<token>.<FIELD>}}, for example
{{tok_abc.CARD_NUMBER}}, and is substituted anywhere in the body.
A placeholder left unresolved fails the request rather than being forwarded.
The response is the target PSP’s response, relayed byte-for-byte,
unless the X-Primer-Proxy-Error response header is present, in which
case Primer itself raised a fault before or after reaching the PSP.
See the default response below for both cases. The proxy never
retries a request to the PSP.
curl --request PUT \
--url https://proxy.primer.io/{proxyPath} \
--header 'Content-Type: application/json' \
--header 'X-Primer-Api-Key: <api-key>' \
--header 'X-Target-Host: <x-target-host>'import requests
url = "https://proxy.primer.io/{proxyPath}"
headers = {
"X-Target-Host": "<x-target-host>",
"X-Primer-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {
'X-Target-Host': '<x-target-host>',
'X-Primer-Api-Key': '<api-key>',
'Content-Type': 'application/json'
}
};
fetch('https://proxy.primer.io/{proxyPath}', 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://proxy.primer.io/{proxyPath}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Primer-Api-Key: <api-key>",
"X-Target-Host: <x-target-host>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://proxy.primer.io/{proxyPath}"
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Add("X-Target-Host", "<x-target-host>")
req.Header.Add("X-Primer-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.put("https://proxy.primer.io/{proxyPath}")
.header("X-Target-Host", "<x-target-host>")
.header("X-Primer-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.asString();require 'uri'
require 'net/http'
url = URI("https://proxy.primer.io/{proxyPath}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-Target-Host"] = '<x-target-host>'
request["X-Primer-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
response = http.request(request)
puts response.read_body{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>",
"forwarded": "false",
"token": "<string>",
"field": "<string>"
}Authorizations
Primer key for the proxy, distinct from the merchant's own PSP credential on the same request.
Headers
Destination host for this request. Must be on both the global Primer allowlist and the merchant's own allowlist, or the proxy returns 403 destination_not_allowed.
"api.stripe.com"
Path Parameters
The target PSP's endpoint path, forwarded to X-Target-Host unchanged, including any further segments and query string.
Body
The request body to forward, with card placeholders substituted.
Response
The target PSP's response, relayed unchanged, unless X-Primer-Proxy-Error is present on the response, in which case the body is a Primer-raised problem document instead (application/problem+json).
Known Primer fault statuses: 400 invalid_request, 401 unauthorized, 403 vault_not_enabled, 403 destination_not_allowed, 404 token_not_found, 405 method_not_allowed, 409 token_consumed, 410 token_expired, 413 payload_too_large, 415 unsupported_content_encoding, 422 unresolved_placeholder / unknown_field / field_not_available / card_data_in_request, 502 psp_unreachable, 503 vault_unavailable, 504 psp_timeout. Any other status is the PSP's own.
RFC 7807 problem document for a Primer-raised proxy fault. Present only when the response carries X-Primer-Proxy-Error: true.
Slug naming the Primer fault, for example token_not_found or unresolved_placeholder.
Short, human-readable summary of the fault.
HTTP status code, repeated from the response.
Human-readable explanation specific to this occurrence.
The request ID, matching X-Primer-Request-Id.
Whether the request reached the PSP. false: it did not, safe to retry once fixed. unknown: it left for the PSP with no usable answer back, retry only with the same PSP idempotency key or check with the PSP first. true: the PSP answered but the proxy could not relay it.
false, unknown, true The token implicated in the fault, where relevant.
The placeholder field implicated in the fault, where relevant.