Token Replacement in Request API
curl --request POST \
--url https://service.pcibooking.net/api/payments/paycard/relay \
--header 'Authorization: <api-key>'import requests
url = "https://service.pcibooking.net/api/payments/paycard/relay"
headers = {"Authorization": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: '<api-key>'}};
fetch('https://service.pcibooking.net/api/payments/paycard/relay', 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://service.pcibooking.net/api/payments/paycard/relay",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: <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"
"net/http"
"io"
)
func main() {
url := "https://service.pcibooking.net/api/payments/paycard/relay"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://service.pcibooking.net/api/payments/paycard/relay")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://service.pcibooking.net/api/payments/paycard/relay")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_bodyThe third-party response is returned as-is. The format and content depend entirely on the destination API.
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Couldn't fetch a valid screening profile:: <profileName>",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Invalid target Uri::",
"errorList": null
}
Token Replacement
Token Replacement in Request API
Replace card tokens with real card data and relay the request to a third-party API.
POST
/
api
/
payments
/
paycard
/
relay
Token Replacement in Request API
curl --request POST \
--url https://service.pcibooking.net/api/payments/paycard/relay \
--header 'Authorization: <api-key>'import requests
url = "https://service.pcibooking.net/api/payments/paycard/relay"
headers = {"Authorization": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: '<api-key>'}};
fetch('https://service.pcibooking.net/api/payments/paycard/relay', 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://service.pcibooking.net/api/payments/paycard/relay",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: <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"
"net/http"
"io"
)
func main() {
url := "https://service.pcibooking.net/api/payments/paycard/relay"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://service.pcibooking.net/api/payments/paycard/relay")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://service.pcibooking.net/api/payments/paycard/relay")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_bodyThe third-party response is returned as-is. The format and content depend entirely on the destination API.
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Couldn't fetch a valid screening profile:: <profileName>",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Invalid target Uri::",
"errorList": null
}
Token Replacement Guide
Replace tokens with card data in API requests
Error Responses
| Code | HTTP Status | Condition |
|---|---|---|
| -125 | 400 | httpMethod not POST/GET/PUT/PATCH/DELETE |
| -160 | 404 | Card not found |
| -150 | 500 | Card retrieval failed |
| -125 | 400 | Profile name provided but profile not found |
| -125 | 400 | Content replacement failed |
| -125 | 400 | Empty content body |
| -125 | 400 | Missing Content-Type header |
| -125 | 400 | Target URI invalid |
| -1003 | 401 | User not owner and not associated |
Error detail
Each condition below gives the exactmoreInfo text, why it happens and how to resolve it. The full set is on the Error Handling page.
-125 Bad input data - profile not found
-125 Bad input data - profile not found
HTTP status: The first is returned by token replacement in a request (See also: Content Filters, Target Profiles
400message: Bad input datamoreInfo: the wording depends on which endpoint you called:Couldn't fetch a valid screening profile:: <profileName>
Couldn't fetch a valid pciShield profile:: <profileName>
paycard/relay). The second is returned by tokenization on response (paycard/capture). They mean the same thing.Reason. The profileName you passed could not be resolved for the account making the request. In almost every case the profile does exist, but on a different account than the one your credentials belong to. A misspelled profile name produces the same error.How to resolve.A PCI Shield profile belongs to the account, not to the sub-user that calls the API. When a sub-user makes a request, the profile is looked up against that sub-user’s parent account. A profile configured on one account is never visible to a sub-user of a different account, and sandbox and production accounts are separate.To check which profiles your credential can see:- Identify the parent account of the sub-user whose credentials you are using.
- Sign in to the PCI Booking portal as that account and open PCI Shield Settings > PCI Shield Profile Settings.
- Confirm the profile name appears there, spelled exactly as you send it. Profile names are matched exactly.
- If the profile is listed under a different account, switch your request to a sub-user of that account rather than copying the profile.
There is no API endpoint that lists the profiles on your own account.
GET /api/booker returns the platform-wide preset tokenization profiles, which are a different set. Your own profiles are visible in the portal only.-125 Bad input data - card data could not be substituted
-125 Bad input data - card data could not be substituted
HTTP status: Reason. The body parsed, but the profile’s selectors did not match anything in it, so no card data was substituted. The request was not relayed. This is a mismatch between the profile and the payload, not a problem with the token.How to resolve.
400message: Bad input datamoreInfo:Replacement of content failed
Could not apply token replacement onto content
- Compare the profile’s selectors against the exact payload you sent. A selector that assumes a different nesting depth or element name matches nothing.
- Check the namespaces. For XML and SOAP, a selector written without namespace handling will not match a namespaced document.
- Confirm the body is the format the profile was written for. A profile written for XML will not match a JSON body.
- Test the profile against a saved copy of a real request before using it in production.
-125 Bad input data - request body is empty
-125 Bad input data - request body is empty
HTTP status: Reason. Token replacement had nothing to work on. The body of the request you asked to be relayed was empty, so there was no content in which to substitute card data.How to resolve.
400message: Bad input datamoreInfo:Content is empty
Content is missing/empty
Empty relay message content
- Send the third-party request you want relayed as the body of the call, not as a query parameter.
- If you are pointing at the content with a parameter name, check that parameter is present and actually carries the payload.
- Check no proxy or client library between you and the API is dropping the body on a
GETrelay.
-125 Bad input data - Content-Type header missing
-125 Bad input data - Content-Type header missing
HTTP status: Reason. Token replacement needs to know how to parse the body before it can find the placeholders. Without a
400message: Bad input datamoreInfo:Content type is missing/empty
Content-Type header it cannot choose a parser.How to resolve.- Set
Content-Typeto match the body you are sending, for exampleapplication/json,text/xml, orapplication/x-www-form-urlencoded. - Send the charset if the third party requires one, for example
text/xml; charset=UTF-8.
-125 Bad input data - unsupported httpMethod value
-125 Bad input data - unsupported httpMethod value
HTTP status: Reason. The
400message: Bad input datamoreInfo:httpMethod should be 'POST','GET','PUT','PATCH','DELETE'
httpMethod parameter names a method the relay does not forward.How to resolve.- Use one of
POST,GET,PUT,PATCHorDELETE, in upper case. - Omit the parameter to accept the default of
POST.
-125 Bad input data - targetUri not usable
-125 Bad input data - targetUri not usable
HTTP status: Reason. The
400message: Bad input datamoreInfo:Target Uri invalid
targetUri could not be parsed as an absolute URL, so the relay had nowhere to send the request.How to resolve.- Send an absolute URL including the scheme, for example
https://api.example.com/path. - URL-encode the value if you pass it as a query parameter, so that its own query string does not terminate yours.
-125 Bad input data - no token found in the custom header
-125 Bad input data - no token found in the custom header
HTTP status: Reason. The relay was told to take the token from the
400message: Bad input datamoreInfo:Token(s) could not be found in custom header X-pciBooking-cardUri
X-pciBooking-cardUri header, but the header was absent or held no readable token.How to resolve.- Add the
X-pciBooking-cardUriheader carrying the full token URI. - Separate multiple tokens as the endpoint documents, and check none of them is empty.
-175 Request timed out - third party did not respond in time
-175 Request timed out - third party did not respond in time
HTTP status:
504message: Request timed outmoreInfo: not populated for this condition. The code and the endpoint are the only signal.Reason. PCI Booking relayed your request but the target server did not answer within the timeout. The failure is on the far side, not in PCI Booking. Card data may already have reached the third party, so the operation cannot be assumed not to have happened.How to resolve.- Raise the
timeoutparameter if the third party is legitimately slow. Check the maximum the endpoint accepts before relying on a high value. - Confirm the target host is reachable and not rate limiting or blocking the call. PCI Booking calls the third party from its own addresses, which the third party may need to allow.
- Do not blind retry a charge. Query the third party for the outcome first. A timeout is an unknown result, not a failure.
-160 Uri not found - token does not exist or was deleted
-160 Uri not found - token does not exist or was deleted
HTTP status: Reason. The token is well formed but no card is stored against it. Either it never existed, or it was deleted. Deletion is permanent and cannot be undone.How to resolve.
404message: Uri not foundmoreInfo:The provided card token does not exist or was already deleted
Token not found
- Confirm the tokenization call that should have created it returned success and returned this exact token.
- Check whether the token was deleted, either explicitly or by a CVV retention policy configured to delete the card on cleanup.
- Check the environment. A token from one environment is not visible in the other.
Some endpoints report a deleted token as
-1003 rather than -160. Treat the two as the same investigation and start with whether the token still exists.-1003 You are not authorized to access this resource - token exists but is not yours to use
-1003 You are not authorized to access this resource - token exists but is not yours to use
HTTP status: Reason. The credential authenticated fine, but it is not allowed to act on the token in the request. The generic wording makes this the single most misread error in the API. On a token call it is far more often one of the causes below than an actual permissions problem.How to resolve.
401message: You are not authorized to access this resource. Please contact customer support.moreInfo: one of the following, depending on which check failed:User is not the owner of this bank card
Merchant or Owner are not associated with bank card [<token>] userID: <userId>
User <userId> is not associated with bank card [<token>]
- Check the token was not deleted. This is the most common cause. Deletion is permanent, and every later call on that token returns
-1003rather than a not-found error. Support can confirm when and by which user a token was deleted. - Check the tokenization actually succeeded. If the call that should have created the token failed, the token never existed, and the first call that uses it reports
-1003. - Check the environment. A sandbox token cannot be used from production, or the reverse.
- Check ownership and association. See the rules below.
- At tokenization, by passing
merchantIdon the tokenizing call. - After tokenization, by associating the token with the merchant.
- An association can only target a primary account. If you pass the ID of a sub-user or a secondary property, the request is rejected and you must associate the token with the parent account instead.
- Tokens never cross environments. A token created in sandbox cannot be used from production, and the reverse is also true.
-1003 even though the credentials are still valid and can still sign in to the portal. The response carries no moreInfo explaining this, so it is indistinguishable from a permissions failure by looking at the response alone.This is a common cause on sandbox accounts, which have a lower allowance than production.Check for it when -1003 appears suddenly across calls that used to work, on more than one token. A block affects every billable call on the account at once, whereas a genuine ownership problem affects only the specific token. Contact support with your account name to have the allowance reviewed and the block lifted.Parameter Constraints
- cardToken must contain a valid card URI with a 32-hex token.
- targetUri is required.
- httpMethod defaults to POST. Accepted values: POST, GET, PUT, PATCH, DELETE.
- postResponseAction accepts
ClearCVVorDeleteToken.
All URLs must be HTTPS. URL-encode all query string components.
Parameters
Authentication
This is a browser-facing endpoint. Use one of the authentication methods below instead of the API key shown above.
string
Recommended. A long-lived token for browser-side calls. How to generate.
string
Alternative. Valid for 5 minutes. How to generate.
Query String
string
required
The token URI identifying the card in PCI Booking.
string
required
The HTTPS URL of the third party to relay the request to.
string
default:"POST"
The HTTP method to use when calling the target URI. One of:
POST, GET, PUT, PATCH, DELETE.string
The ID of a target profile configured for this request. If omitted, PCI Booking uses placeholder-based replacement instead.
string
For placeholder-based replacement: the key containing card data in form-data or query string. Ignored if
profileName is provided. If omitted, PCI Booking searches the request body for placeholders.string
Action to perform after a successful relay.
ClearCVV clears the CVV from the token. DeleteToken deletes the token entirely. If omitted, no action is taken.int
Seconds to wait for the third-party response before timing out.
Headers
string
Compression format:
gzip or deflate. Omit if no compression is needed.Request Body
The request body is relayed to the third party as-is, with token placeholders replaced by real card data. Structure it as required by the third party’s API.Placeholders
Write placeholders in the body (or query string) as$~Name~$. Names are case-insensitive. Supported placeholders:
| Placeholder | Replaced with |
|---|---|
$~Number~$ | Card number (PAN) |
$~CardType~$ | Card brand name |
$~ExpirationMM~$ | Expiration month, two digits (e.g. 07) |
$~ExpirationM~$ | Expiration month, no leading zero (e.g. 7) |
$~ExpirationYYYY~$ | Expiration year, four digits |
$~ExpirationYY~$ | Expiration year, two digits |
$~CVV~$ | Security code (subject to the token’s CVV retention policy) |
$~OwnerName~$ | Cardholder name |
$~OwnerID~$ | Owner ID |
$~IssueNumber~$ | Card issue number |
$~ThreeDS_AuthenticationValue~$ | 3DS authentication value (CAVV/AAV) |
$~ThreeDS_Eci~$ | 3DS Electronic Commerce Indicator |
$~ThreeDS_XID~$ | 3DS v1 transaction identifier |
$~ThreeDS_ACS~$ | 3DS v2 ACS transaction ID |
$~ThreeDS_Universal_TransactionId~$ | 3DS universal transaction ID |
$~ThreeDS_Version~$ | 3DS protocol version |
$~ThreeDS_MerchantName~$ | 3DS merchant name |
$~ThreeDS_SLI~$ | 3DS Security Level Indicator |
$~ThreeDS_EWallet~$ | 3DS eWallet indicator |
profileName instead of embedding placeholders - see the second example below.
Request Example
Send a JSON payment request to a third-party API, with PCI Booking replacing the card placeholders with real data before forwarding:- curl
- Node.js
- Python
curl -X POST "https://service.pcibooking.net/api/payments/paycard/relay?cardToken=https%3A%2F%2Fservice.pcibooking.net%2Fapi%2Fpayments%2Fpaycard%2F555fd7b49f134b42a5dbe4d576b2e527&targetUri=https%3A%2F%2Fapi.thirdparty.com%2Fv1%2Fpayments&httpMethod=POST" \
-H "Authorization: APIKEY your-api-key" \
-H "Content-Type: application/json" \
-d '{
"amount": 250.00,
"currency": "USD",
"card": {
"number": "$~Number~$",
"expMonth": "$~ExpirationMM~$",
"expYear": "$~ExpirationYYYY~$",
"cvv": "$~CVV~$",
"holderName": "$~OwnerName~$"
},
"reference": "order-98765"
}'
const params = new URLSearchParams({
cardToken: "https://service.pcibooking.net/api/payments/paycard/555fd7b49f134b42a5dbe4d576b2e527",
targetUri: "https://api.thirdparty.com/v1/payments",
httpMethod: "POST"
});
const response = await fetch(
`https://service.pcibooking.net/api/payments/paycard/relay?${params}`,
{
method: "POST",
headers: {
"Authorization": "APIKEY your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 250.00,
currency: "USD",
card: {
number: "$~Number~$",
expMonth: "$~ExpirationMM~$",
expYear: "$~ExpirationYYYY~$",
cvv: "$~CVV~$",
holderName: "$~OwnerName~$"
},
reference: "order-98765"
})
}
);
const data = await response.text();
console.log(data);
import requests
response = requests.post(
"https://service.pcibooking.net/api/payments/paycard/relay",
params={
"cardToken": "https://service.pcibooking.net/api/payments/paycard/555fd7b49f134b42a5dbe4d576b2e527",
"targetUri": "https://api.thirdparty.com/v1/payments",
"httpMethod": "POST"
},
headers={
"Authorization": "APIKEY your-api-key",
"Content-Type": "application/json"
},
json={
"amount": 250.00,
"currency": "USD",
"card": {
"number": "$~Number~$",
"expMonth": "$~ExpirationMM~$",
"expYear": "$~ExpirationYYYY~$",
"cvv": "$~CVV~$",
"holderName": "$~OwnerName~$"
},
"reference": "order-98765"
}
)
print(response.text)
$~Number~$, $~ExpirationMM~$, $~ExpirationYYYY~$, $~CVV~$, and $~OwnerName~$ placeholders with the actual card data from the token before forwarding the request to api.thirdparty.com. See the full placeholder list above.
To use a target profile (pre-configured replacement rules) instead of inline placeholders:
- curl
- Node.js
- Python
curl -X POST "https://service.pcibooking.net/api/payments/paycard/relay?cardToken=https%3A%2F%2Fservice.pcibooking.net%2Fapi%2Fpayments%2Fpaycard%2F555fd7b49f134b42a5dbe4d576b2e527&targetUri=https%3A%2F%2Fapi.thirdparty.com%2Fv1%2Fpayments&httpMethod=POST&profileName=my-thirdparty-profile" \
-H "Authorization: APIKEY your-api-key" \
-H "Content-Type: application/json" \
-d '{
"amount": 250.00,
"currency": "USD",
"reference": "order-98765"
}'
const params = new URLSearchParams({
cardToken: "https://service.pcibooking.net/api/payments/paycard/555fd7b49f134b42a5dbe4d576b2e527",
targetUri: "https://api.thirdparty.com/v1/payments",
httpMethod: "POST",
profileName: "my-thirdparty-profile"
});
const response = await fetch(
`https://service.pcibooking.net/api/payments/paycard/relay?${params}`,
{
method: "POST",
headers: {
"Authorization": "APIKEY your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 250.00,
currency: "USD",
reference: "order-98765"
})
}
);
const data = await response.text();
console.log(data);
import requests
response = requests.post(
"https://service.pcibooking.net/api/payments/paycard/relay",
params={
"cardToken": "https://service.pcibooking.net/api/payments/paycard/555fd7b49f134b42a5dbe4d576b2e527",
"targetUri": "https://api.thirdparty.com/v1/payments",
"httpMethod": "POST",
"profileName": "my-thirdparty-profile"
},
headers={
"Authorization": "APIKEY your-api-key",
"Content-Type": "application/json"
},
json={
"amount": 250.00,
"currency": "USD",
"reference": "order-98765"
}
)
print(response.text)
Response
200 - The third-party response, relayed back as-is.Consider adding business logic based on the CVV retention policy status after a token replacement request.
The third-party response is returned as-is. The format and content depend entirely on the destination API.
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Couldn't fetch a valid screening profile:: <profileName>",
"errorList": null
}
{
"code": -125,
"message": "Bad input data",
"moreInfo": "Invalid target Uri::",
"errorList": null
}

