curl --request POST \
--url https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped \
--header 'Api-Key: <api-key>' \
--header 'Content-Type: application/json' \
--header 'sessionid: <sessionid>' \
--data '
{
"amountInUSDCents": 2,
"bufferPercentage": 10,
"expiresAt": "2023-11-07T05:31:56Z",
"allowedMccs": [
"<string>"
],
"allowedMerchants": [
"<string>"
]
}
'import requests
url = "https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped"
payload = {
"amountInUSDCents": 2,
"bufferPercentage": 10,
"expiresAt": "2023-11-07T05:31:56Z",
"allowedMccs": ["<string>"],
"allowedMerchants": ["<string>"]
}
headers = {
"sessionid": "<sessionid>",
"Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
sessionid: '<sessionid>',
'Api-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amountInUSDCents: 2,
bufferPercentage: 10,
expiresAt: '2023-11-07T05:31:56Z',
allowedMccs: ['<string>'],
allowedMerchants: ['<string>']
})
};
fetch('https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped', 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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped",
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([
'amountInUSDCents' => 2,
'bufferPercentage' => 10,
'expiresAt' => '2023-11-07T05:31:56Z',
'allowedMccs' => [
'<string>'
],
'allowedMerchants' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Api-Key: <api-key>",
"Content-Type: application/json",
"sessionid: <sessionid>"
],
]);
$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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped"
payload := strings.NewReader("{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("sessionid", "<sessionid>")
req.Header.Add("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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped")
.header("sessionid", "<sessionid>")
.header("Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["sessionid"] = '<sessionid>'
request["Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"encryptedPan": {
"iv": "<string>",
"data": "<string>"
},
"encryptedCvc": {
"iv": "<string>",
"data": "<string>"
},
"last4": "<string>",
"expirationMonth": "<string>",
"expirationYear": "<string>",
"status": "notActivated"
}Create a scoped card for a user
Creates a virtual card scoped to a single transaction, optimized for AI agent usage. This endpoint is protected and requires tenant-level access—contact Rain to enable it during onboarding.
The card is created with a lifetime spending limit, an optional expiry, and optional merchant-category (allowedMccs) and merchant-name (allowedMerchants) allow-lists and returns encrypted card details (PAN and CVC) in the response. By default, a 1.2x ceiling is applied to the spending limit to buffer for authorization holds; this buffer percentage can be configured during onboarding. Requires the sessionid header containing an encrypted session ID for retrieving the encrypted card details.
User-level limits (default values shown; all limits are configurable during onboarding):
- Maximum 10 active scoped cards per user
- Maximum 10 scoped cards created per user within a rolling 24-hour window
- Maximum $5,000 approved spend across all of a user’s scoped cards within a rolling 24-hour window
Contact Rain’s team during onboarding to configure custom limits for active cards, creation velocity, daily spend, and the authorization hold buffer.
Card creation fails with a 400 error if the active card limit or creation velocity limit is reached. The daily spend limit is enforced at authorization time—transactions that would exceed the limit are declined with reason scoped_daily_spend_limit_exceeded.
curl --request POST \
--url https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped \
--header 'Api-Key: <api-key>' \
--header 'Content-Type: application/json' \
--header 'sessionid: <sessionid>' \
--data '
{
"amountInUSDCents": 2,
"bufferPercentage": 10,
"expiresAt": "2023-11-07T05:31:56Z",
"allowedMccs": [
"<string>"
],
"allowedMerchants": [
"<string>"
]
}
'import requests
url = "https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped"
payload = {
"amountInUSDCents": 2,
"bufferPercentage": 10,
"expiresAt": "2023-11-07T05:31:56Z",
"allowedMccs": ["<string>"],
"allowedMerchants": ["<string>"]
}
headers = {
"sessionid": "<sessionid>",
"Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
sessionid: '<sessionid>',
'Api-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amountInUSDCents: 2,
bufferPercentage: 10,
expiresAt: '2023-11-07T05:31:56Z',
allowedMccs: ['<string>'],
allowedMerchants: ['<string>']
})
};
fetch('https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped', 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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped",
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([
'amountInUSDCents' => 2,
'bufferPercentage' => 10,
'expiresAt' => '2023-11-07T05:31:56Z',
'allowedMccs' => [
'<string>'
],
'allowedMerchants' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Api-Key: <api-key>",
"Content-Type: application/json",
"sessionid: <sessionid>"
],
]);
$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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped"
payload := strings.NewReader("{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("sessionid", "<sessionid>")
req.Header.Add("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://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped")
.header("sessionid", "<sessionid>")
.header("Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-dev.rain.xyz/v1/issuing/users/{userId}/cards/scoped")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["sessionid"] = '<sessionid>'
request["Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amountInUSDCents\": 2,\n \"bufferPercentage\": 10,\n \"expiresAt\": \"2023-11-07T05:31:56Z\",\n \"allowedMccs\": [\n \"<string>\"\n ],\n \"allowedMerchants\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"encryptedPan": {
"iv": "<string>",
"data": "<string>"
},
"encryptedCvc": {
"iv": "<string>",
"data": "<string>"
},
"last4": "<string>",
"expirationMonth": "<string>",
"expirationYear": "<string>",
"status": "notActivated"
}Authorizations
Headers
Encrypted session ID for retrieving the encrypted card details
1Path Parameters
ID of the user to create a scoped card for
Body
Scoped card configuration
The desired spending limit in USD cents. By default, a 1.2x ceiling is applied to buffer for authorization holds (configurable during onboarding).
x >= 1Spend headroom over amountInUSDCents, in whole percent. Omit for the default of 20. Set 0 to disable the buffer so the card's limit equals the amount exactly.
0 <= x <= 20BIN the card is issued on. Defaults to consumer. commercial requires corporate card issuance to be enabled for your tenant.
consumer, commercial Optional absolute expiry (ISO-8601 with UTC offset, at most 365 days in the future). After this time Rain declines new authorizations on the card; refunds and other credits are exempt.
Optional merchant-category allow-list of four-digit MCCs. Authorizations at merchants outside the list are declined with reason scoped_card_mcc_not_allowed; refunds and other credits are exempt.
1^[0-9]{4}$Optional merchant allow-list of up to 25 merchant names (each at most 64 characters after trimming; no duplicates or blank entries). Rain matches each authorization's merchant name against the list and declines non-matching merchants with reason merchant_scope_mismatch. Matching is a best-effort name match, and refunds, other credits, and $0 account verifications are exempt.
1 - 25 elements1 - 64Response
Successful operation
The card ID. Can be used to retry the /issuing/cards/{cardId}/secrets endpoint if the initial encrypted details retrieval fails.
The encrypted PAN
Show child attributes
Show child attributes
The encrypted CVC
Show child attributes
Show child attributes
The last 4 digits of the card number
The card's expiration month
The card's expiration year
notActivated, active, locked, canceled