Store Paycard (Card Migration)
curl --request POST \
--url https://service.pcibooking.net/api/payments/paycard \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"BankCardDetails": {}
}
'import requests
url = "https://service.pcibooking.net/api/payments/paycard"
payload = { "BankCardDetails": {} }
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({BankCardDetails: {}})
};
fetch('https://service.pcibooking.net/api/payments/paycard', 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",
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([
'BankCardDetails' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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://service.pcibooking.net/api/payments/paycard"
payload := strings.NewReader("{\n \"BankCardDetails\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
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")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"BankCardDetails\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://service.pcibooking.net/api/payments/paycard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"BankCardDetails\": {}\n}"
response = http.request(request)
puts response.read_body{
"Type": "Visa",
"Number": "491891******5005",
"NameOnCard": "Juan Dela Cruz",
"ExpirationDate": {
"Month": "07",
"Year": "2020"
},
"IssueNumber": "2"
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected APIKEY or Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Merchant or Owner are not associated with bank card [{{Token}] userID: SoBookIt",
"errorList": null
}
Capture Cards
Store Paycard (Card Migration)
Store card details in PCI Booking when migrating from local storage. The card data is sent in XML format.
POST
/
api
/
payments
/
paycard
Store Paycard (Card Migration)
curl --request POST \
--url https://service.pcibooking.net/api/payments/paycard \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"BankCardDetails": {}
}
'import requests
url = "https://service.pcibooking.net/api/payments/paycard"
payload = { "BankCardDetails": {} }
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({BankCardDetails: {}})
};
fetch('https://service.pcibooking.net/api/payments/paycard', 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",
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([
'BankCardDetails' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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://service.pcibooking.net/api/payments/paycard"
payload := strings.NewReader("{\n \"BankCardDetails\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
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")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"BankCardDetails\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://service.pcibooking.net/api/payments/paycard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"BankCardDetails\": {}\n}"
response = http.request(request)
puts response.read_body{
"Type": "Visa",
"Number": "491891******5005",
"NameOnCard": "Juan Dela Cruz",
"ExpirationDate": {
"Month": "07",
"Year": "2020"
},
"IssueNumber": "2"
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected APIKEY or Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Merchant or Owner are not associated with bank card [{{Token}] userID: SoBookIt",
"errorList": null
}
Card Migration Guide
Migrate existing card data into PCI Booking tokens
Sending raw card data through this endpoint puts your system in PCI DSS scope, since it requires handling raw card numbers to call it. This endpoint is intended for a one-time migration from a system that is already PCI DSS compliant - it is not recommended as an ongoing capture method for production traffic. Use a card capture method that keeps card data off your systems instead. See Card Migration for the full guidance.
This endpoint accepts card details in XML format only. Set
Content-Type: text/xml.Error Responses
| Code | HTTP Status | Condition |
|---|---|---|
| -123 | 400 | Request body is not valid XML or is null |
| -123 | 400 | XML fails schema validation |
| -160 | 404 | Submitted data is PaymentInfo instead of BankCard |
| -179 | 400 | Card validation failed (Luhn check, expiration, or CVV). Returns errorList with details |
| -125 | 400 | Card storage failed due to invalid data |
| -150 | 500 | Card storage failed due to a system error |
| -1003 | 401 | Missing CanTokenize permission |
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.
-123 Message badly formatted - body is not valid XML for this endpoint
-123 Message badly formatted - body is not valid XML for this endpoint
HTTP status: Reason. The endpoint expects XML that validates against its schema, and the body either did not parse as XML or failed validation. When validation is the cause, the failing details are listed in
400message: Message badly formattedmoreInfo:Bad XML document
Bad XML document. Errors: <validation errors>
moreInfo.How to resolve.- Read the list in
moreInfo. It names the elements that failed, which is usually enough on its own. - Check element order. The schema is sequence-sensitive, so correctly named elements in the wrong order still fail.
- Check the declared encoding matches what you actually sent.
-125 Bad input data - expiration date rejected
-125 Bad input data - expiration date rejected
HTTP status: Reason. The expiration month or year is missing, out of range, or in a format the endpoint does not accept.How to resolve.
400message: Bad input datamoreInfo:Expiration year/month error
- Send the month as two digits,
01through12. - Check the year format the endpoint expects. Some accept two digits and some four, and they are not interchangeable.
- Check the date is not in the past. An expired card is rejected at tokenization.
-179 Bad input parameter - CVV format rejected
-179 Bad input parameter - CVV format rejected
HTTP status: Reason. The CVV was present but did not match three or four digits. The API applies the same rule to every card brand.How to resolve.
400message: Bad input parametermoreInfo:Invalid CVV value: <value>
- Send three or four digits, digits only, with no spaces or punctuation. The API accepts either length for any brand - it does not require four digits for American Express, and it does not reject four digits on other brands.
- Omit the field entirely rather than sending an empty string. An absent or empty CVV is accepted, and the card is simply stored without one.
The hosted card entry form is stricter than the API. Where it has detected the brand it accepts exactly three digits for every brand except American Express, which takes three or four; where the brand is unknown it accepts three or four. A CVV that the form rejects would therefore have been accepted had you sent it to the API directly.
-1003 You are not authorized to access this resource - credential not accepted
-1003 You are not authorized to access this resource - credential not accepted
HTTP status: The two expiration variants apply to one-time access tokens: the first means the token has passed its expiry, the second that the requested lifetime exceeded the maximum allowed and was refused outright rather than capped.Reason. Authentication itself failed: the API key or session token was not recognised, has expired, or belongs to a different environment than the endpoint you called.How to resolve.
401message: You are not authorized to access this resource. Please contact customer support.moreInfo:Invalid API Key <key>
Expiration time invalid: Already expired
Expiration time invalid: too late
- Confirm the
APIKEYprefix is present and there is a single space between it and the key. - Confirm the key belongs to the same environment as the host you called. Sandbox and production keys are not interchangeable.
- If you are using a session token, check it has not passed its time to live and start a new session if it has.
Parameter Constraints
| Parameter | Type | Required | Constraints |
|---|---|---|---|
| validateLuhn | boolean | No | Defaults to true. Validates card number with Luhn algorithm |
| validateExpiration | boolean | No | Defaults to false. When true, rejects expired cards |
| saveCVV | boolean | No | Defaults to false. Requires CVV retention policy to be set after storage |
| eliminateCardDuplication | boolean | No | Defaults to false. When true, returns existing token if card already stored (same card number + expiration date) |
| Content-Type | string | Yes | Must be text/xml |
| Auth | string | Yes | ApiKey, AccessToken, or SessionToken |
BankCardDetails XML document. See Card Data XML Structure for the full field reference.
<BankCardDetails xmlns="http://www.pcibooking.net/reservation" schemaVersion="1.0">
<BankCard>
<Type>Visa</Type>
<Number>4918914107195005</Number>
<NameOnCard>Juan Dela Cruz</NameOnCard>
<ExpirationDate>
<Month>07</Month>
<Year>2020</Year>
</ExpirationDate>
<IssueNumber>2</IssueNumber>
<OwnerID>123456789</OwnerID>
<CVV>123</CVV>
</BankCard>
</BankCardDetails>
Location response header. The response body contains the submitted card details with sensitive data masked.
Parameters
Headers
string
required
Your API key prefixed with
APIKEY. Example: APIKEY your-api-key. For server-to-server calls.Alternative: Browser-Side Authentication
Alternative: Browser-Side Authentication
This endpoint also accepts token-based authentication via query parameters:
If multiple methods are provided, precedence: Session Token > Access Token > API Key.
| Method | Details |
|---|---|
| Access Token (recommended) | accessToken query param. How to generate. |
| Session Token | sessionToken query param. How to generate. Valid for 5 minutes. |
string
default:"text/xml"
required
Must be
text/xml. This endpoint accepts card details in XML format only.Query String
string
A reference value that can be used to query for this card token.
string
User ID of the property to associate the token with. Found under “Property settings” in the user’s site.
string
User ID of the PCI Booking customer (booker ID) to associate the token with. The customer must share their PCI Booking user ID with you.
boolean
default:"false"
Whether to save the CVV in the database. true - save the CVV. false - do not save the CVV.
boolean
default:"false"
When
true, PCI Booking checks whether this card already exists as a token in your account (same card number + expiration date) and returns the existing token instead of creating a new one.boolean
default:"false"
Whether to check if the expiration date is valid.
True validates the expiration date and tokenization will fail if expired. False accepts the date as is, even if in the past.boolean
default:"true"
Whether to check if the card number passes the Luhn algorithm.
True validates using Luhn and tokenization will fail if the number does not pass. False accepts the number as is.Request Body
object
The BankCardDetails object. See the XML structure and example above.
Request Example
Store a Visa card and associate it with a reference and property:- curl
- Node.js
- Python
curl -X POST "https://service.pcibooking.net/api/payments/paycard?ref=booking-12345&merchant=hotel-sunrise&saveCVV=true" \
-H "Authorization: APIKEY your-api-key" \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="utf-8"?>
<BankCardDetails xmlns="http://www.pcibooking.net/reservation" schemaVersion="1.0">
<BankCard>
<Type>Visa</Type>
<Number>4111111111111111</Number>
<NameOnCard>Jane Smith</NameOnCard>
<ExpirationDate>
<Month>12</Month>
<Year>2028</Year>
</ExpirationDate>
<CVV>123</CVV>
</BankCard>
</BankCardDetails>'
const xmlBody = `<?xml version="1.0" encoding="utf-8"?>
<BankCardDetails xmlns="http://www.pcibooking.net/reservation" schemaVersion="1.0">
<BankCard>
<Type>Visa</Type>
<Number>4111111111111111</Number>
<NameOnCard>Jane Smith</NameOnCard>
<ExpirationDate>
<Month>12</Month>
<Year>2028</Year>
</ExpirationDate>
<CVV>123</CVV>
</BankCard>
</BankCardDetails>`;
const params = new URLSearchParams({
ref: "booking-12345",
merchant: "hotel-sunrise",
saveCVV: "true"
});
const response = await fetch(
`https://service.pcibooking.net/api/payments/paycard?${params}`,
{
method: "POST",
headers: {
"Authorization": "APIKEY your-api-key",
"Content-Type": "text/xml"
},
body: xmlBody
}
);
const tokenUri = response.headers.get("Location");
console.log("Token URI:", tokenUri);
const data = await response.json();
console.log(data);
import requests
xml_body = """<?xml version="1.0" encoding="utf-8"?>
<BankCardDetails xmlns="http://www.pcibooking.net/reservation" schemaVersion="1.0">
<BankCard>
<Type>Visa</Type>
<Number>4111111111111111</Number>
<NameOnCard>Jane Smith</NameOnCard>
<ExpirationDate>
<Month>12</Month>
<Year>2028</Year>
</ExpirationDate>
<CVV>123</CVV>
</BankCard>
</BankCardDetails>"""
response = requests.post(
"https://service.pcibooking.net/api/payments/paycard",
params={
"ref": "booking-12345",
"merchant": "hotel-sunrise",
"saveCVV": "true"
},
headers={
"Authorization": "APIKEY your-api-key",
"Content-Type": "text/xml"
},
data=xml_body
)
token_uri = response.headers.get("Location")
print("Token URI:", token_uri)
print(response.json())
Location response header (e.g. Location: https://service.pcibooking.net/api/payments/paycard/555fd7b49f134b42a5dbe4d576b2e527).
Response
201 - Card stored. ALocation header is returned with the token URI. The response body contains the card details with sensitive data masked.
Remember to set the CVV Retention Policy on the token if you stored the CVV.
{
"Type": "Visa",
"Number": "491891******5005",
"NameOnCard": "Juan Dela Cruz",
"ExpirationDate": {
"Month": "07",
"Year": "2020"
},
"IssueNumber": "2"
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Bad or missing authorization data, expected APIKEY or Temp Session or One-Time Accesss Token",
"errorList": null
}
{
"code": -1003,
"message": "Not authorized to access this resource",
"moreInfo": "Merchant or Owner are not associated with bank card [{{Token}] userID: SoBookIt",
"errorList": null
}

