CMS Forms Submission API
Reference for posting submissions to a CMS form from your own code. Audience: developers integrating CMS donations into a website, mobile app, or back-office system.
Introduction
The Forms Submission API lets you create a submission against any CMS form from your own server or application. CMS records the resulting bill and, where applicable, processes the payment — just as if the submission had been made through the form's own page.
There are three submission modes. The mode is selected by the shape of your request body:
- Charge now — you send the donor's card details; CMS processes the payment and records a paid bill.
- Already paid — you collected the payment elsewhere (your own payment processor, in-person, cash, check, etc.); CMS records a paid bill referencing your external transaction.
- Unpaid pledge — you record an intent to give with no payment; CMS opens an outstanding bill that can be settled later.
Endpoint URL
All requests are sent to your organization's CMS subdomain. If your CMS site is hosted at https://your-organization.chabadms.com, that is the base URL for every call.
| Field |
Value |
| Base URL |
https://your-organization.chabadms.com |
| Path |
/api/Forms/AddSubmissionApi |
| Method |
POST |
Replace your-organization with your actual CMS subdomain in every sample on this page.
Authentication
Each form has its own API token. Authentication is performed by including this token in the request body — there is no separate Authorization header, cookie, or session to manage.
- In CMS, open the form's settings page and copy the form's API token.
- In your code, include that token as the
ApiToken field on every request body.
- If the token doesn't match, the submission is rejected with a token-validation error.
Treat the token as a secret. Anyone with the token can submit to the form, which can include charging cards. Do not commit it to source control or expose it in client-side JavaScript that runs in untrusted browsers. Rotate it from the form's settings page if you suspect it has leaked.
Request envelope
POST https://your-organization.chabadms.com/api/Forms/AddSubmissionApi
| Header |
Value |
Content-Type |
application/json |
Accept |
application/json (recommended) |
The body is a JSON object. Two of its properties — Fields and Transaction — are themselves JSON arrays, but they must be sent as JSON-encoded strings on the outer envelope, not as nested JSON arrays.
In every language sample below, you build Fields (and, where used, Transaction) as a native list or array, then serialize that array to a string with JSON.stringify / json.dumps / JsonConvert.SerializeObject / json_encode before assigning it to the outer body.
Top-level fields
The top-level JSON body has these properties:
| Field |
Type |
Required |
Description |
FormId |
GUID (string) |
Required |
The form to submit to. Get this value from the form's settings page. |
ApiToken |
string |
Required |
The form's API token (see Authentication). Comparison is case-sensitive. |
Fields |
string (JSON-encoded array) |
Required |
JSON-encoded array of form-field objects. See Fields[] schema. At minimum include contactEmail, firstName, lastName, and (for payment forms) paymentAmount. |
Transaction |
string (JSON-encoded array) |
Optional |
JSON-encoded array of transaction objects. Send this for Mode 2 (already paid) or to control recurring schedules. See Transaction[] schema. |
Amount |
decimal |
Optional |
Total amount. If omitted, it is taken from paymentAmount in Fields or from Transaction[].Amount. |
Email |
string |
Optional |
Top-level contact email. If omitted, the value of contactEmail from Fields is used. |
FamName |
string |
Optional |
Family name override; otherwise built from firstName + lastName. |
PayId |
integer |
Optional |
An existing saved-payment-profile ID for the donor. Use to charge a card already on file instead of supplying card details. The ID is available from your CMS admin. |
BillId |
integer |
Optional |
An existing bill ID. Use to post a payment against a bill that already exists in CMS rather than creating a new bill. |
ClientIP |
string |
Optional |
Originating client IP, for your own audit records. May be omitted. |
Any other JSON properties you include on the outer body are ignored.
Fields[] schema
Each element in the Fields array has three properties:
| Property |
JSON key |
Type |
Description |
| Type |
type |
string |
Field identifier. Case-insensitive. Drives how the server interprets the value. |
| Name |
name |
string |
Human-readable label. Not interpreted server-side; pass any value or omit. |
| Value |
value |
any |
The data. Most fields are strings; reservationList is an array; numeric fields accept either a number or a numeric string. |
Recognized field types
Send only the field types you need. Any unrecognized type is captured in the submission record but does not affect bill/payment processing.
| Category |
Type values |
Notes |
| Contact |
contactEmail, contactPhone, firstName, lastName, nameOnCard |
contactEmail drives receipts. nameOnCard overrides firstName+lastName on the credit-card record. |
| Amount |
paymentAmount, feeAmount, paymentNote, paymentSubcategoryId |
paymentAmount drives the bill total. paymentSubcategoryId is the destination accounting category (numeric). |
| Card |
cardNumber, cardExpMonth, cardExpYear, cvv, cardCVV, cardType, cardCompany |
Year accepts YY or YYYY. cardType is auto-detected if omitted. See Tokenization & PCI for the safer alternative. |
| Billing address (card) |
cardAddress, cardAddressCity, cardAddressState, cardAddressZip, cardEmail |
AVS data sent to the processor. Improves authorization rates. |
| Contact address |
contactAddress, contactCity, contactState, contactZip |
Address fields for the donor/contact, independent of the card billing address. Use when the form captures contact info without taking a card. |
| Tokenization |
opaqueDataDescriptor, opaqueDataValue, payrixToken |
Use these instead of raw card fields when tokenizing on the client. opaqueData* = Authorize.Net Accept.js; payrixToken = Payrix. |
| Recurring |
recurringPeriod, recurringNumber |
recurringPeriod sets the schedule code (1=Once, 2=Weekly, 3=Monthly, 4=Quarterly, 5=Yearly). recurringNumber is the installment count. Pair with the equivalent properties on Transaction[] when sending one. |
| Address (legacy) |
billingAddress, billingZip, state, country |
Older address field names; still recognized for legacy form layouts. |
| Linking |
InvoiceNo, BillId, FamilyId |
When you want the submission to attach to an existing bill or family. |
| Promo |
PromoCode, PromoDiscount, PromoDiscountType |
Promo code is validated server-side against the form's configured codes. |
| Reservations |
reservationList |
Value is a JSON array of reservation items (event registration use case). Out of scope for standard donation flows. |
Example Fields[] payload
[
{ "type": "contactEmail", "value": "donor@example.com" },
{ "type": "firstName", "value": "Jane" },
{ "type": "lastName", "value": "Doe" },
{ "type": "paymentAmount", "value": "50.00" },
{ "type": "paymentNote", "value": "General donation" }
]
Transaction[] schema
Send a Transaction array when you want explicit control over how the bill and payment are recorded — in particular for Mode 2 (already paid) or to schedule recurring billing.
| Property |
Type |
Description |
IsCharged |
boolean |
Mode selector. true = payment was already captured by you externally; CMS records it as paid. false (or omitted) = let CMS process the card based on the card fields in Fields (or leave the bill unpaid if no card fields were supplied). |
Amount |
decimal |
Transaction amount. If omitted, the value of paymentAmount from Fields is used. |
FeeAmount |
decimal |
Convenience-fee component. Stored separately on the bill. |
Note |
string |
Bill memo. Visible on the bill detail screen and searchable in CMS reporting. |
Subcategory |
integer |
Accounting subcategory ID. If omitted, the form's default subcategory is used. |
ChargeResponse |
object |
External-charge details, used when IsCharged: true. Embed your own reference data here (for example AuthorizeTransId, ResponseCode, Success) so the payment record carries that reference. May be null — if so, record the external reference in Note instead. |
PaymentProfileId |
string |
Optional reference to a saved payment profile on your side. |
ChargeDate |
datetime |
When the external charge occurred. ISO-8601 string. Defaults to the time the request is received. |
BillPeriod |
integer or string |
Recurrence schedule. Accepts either the integer code or the name: 1/"Once", 2/"Weekly", 3/"Monthly", 4/"Quarterly", 5/"Yearly". Default: Once. See Recurring billing. |
NumOfPayments |
integer |
Number of installments. Only meaningful when BillPeriod is not Once. |
TransactionType |
string |
Optional. Values: Regular, Fee, GuestReservation. Defaults to Regular. |
Example Transaction[] payload (already-paid import)
[
{
"IsCharged": true,
"Amount": 50.00,
"Note": "Stripe charge ch_3ABC123",
"Subcategory": 123,
"ChargeDate": "2026-05-10T14:32:00Z",
"BillPeriod": "Once",
"ChargeResponse": {
"AuthorizeTransId": "ch_3ABC123",
"ResponseCode": "Approved",
"Success": true
}
}
]
Response shape
All responses are JSON with two fields:
{
"StatusCode": "OK", // "OK", "BadRequest", "Forbidden", etc.
"Errors": [] // array of error messages; empty on success
}
A successful submission returns HTTP 200 with StatusCode: "OK" and an empty Errors array. Validation, authentication, or processing errors return HTTP 400 with one or more strings in Errors.
The response does not return the resulting bill ID or payment ID. If you need to reconcile records between your system and CMS, embed your own reference (an order number, an external transaction ID, etc.) in a field your team can find later — for example in paymentNote or in Transaction[].Note.
Errors & HTTP codes
| HTTP |
StatusCode |
Cause |
Typical Errors[] content |
| 200 |
OK |
Success |
[] |
| 400 |
BadRequest |
Token mismatch |
["Your request didn't pass API Token validation"] |
| 400 |
BadRequest |
Missing or invalid request data |
e.g. ["Can't parse amount"], ["Form not found"], ["Form is not configured for payments"] |
| 400 |
BadRequest |
Card declined by the payment processor (Mode 1) |
e.g. ["The card was declined"], ["Invalid card number"] |
| 400 |
— |
Unexpected error |
Plain text body "SUBMISSION DECLINED" |
Recommended client handling:
- If HTTP status is 200 and the JSON body's
StatusCode == "OK" and Errors is empty — treat as success.
- Otherwise, surface every string in
Errors to your operator UI / log. Do not retry on validation or token errors — the same payload will fail again.
- For network-level errors (timeouts, 5xx) you may retry with backoff; see Idempotency & retries.
Mode 1: Charge now
You send the donor's card and billing details inside Fields. CMS submits the charge to the merchant processor configured on the form. On approval, a paid bill is recorded. On decline, no bill is recorded and the failure message is returned in Errors.
To use this mode: include card fields in Fields, and either omit Transaction or include it with IsCharged: false.
Sample request
curl -X POST "https://your-organization.chabadms.com/api/Forms/AddSubmissionApi" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": "[{\"type\":\"contactEmail\",\"value\":\"jane@example.com\"},{\"type\":\"firstName\",\"value\":\"Jane\"},{\"type\":\"lastName\",\"value\":\"Doe\"},{\"type\":\"paymentAmount\",\"value\":\"50.00\"},{\"type\":\"paymentNote\",\"value\":\"General donation\"},{\"type\":\"cardNumber\",\"value\":\"4111111111111111\"},{\"type\":\"cardExpMonth\",\"value\":\"12\"},{\"type\":\"cardExpYear\",\"value\":\"2028\"},{\"type\":\"cvv\",\"value\":\"123\"},{\"type\":\"cardAddressZip\",\"value\":\"10001\"}]"
}'
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class CmsClient
{
private static readonly HttpClient _http = new HttpClient();
public async Task<string> SubmitChargeNowAsync()
{
var fields = new[]
{
new { type = "contactEmail", value = "jane@example.com" },
new { type = "firstName", value = "Jane" },
new { type = "lastName", value = "Doe" },
new { type = "paymentAmount", value = "50.00" },
new { type = "paymentNote", value = "General donation" },
new { type = "cardNumber", value = "4111111111111111" },
new { type = "cardExpMonth", value = "12" },
new { type = "cardExpYear", value = "2028" },
new { type = "cvv", value = "123" },
new { type = "cardAddressZip",value = "10001" }
};
var body = new
{
FormId = "00000000-0000-0000-0000-000000000000",
ApiToken = "YOUR_FORM_API_TOKEN",
Fields = JsonConvert.SerializeObject(fields) // inner array must be a JSON-encoded string
};
var json = JsonConvert.SerializeObject(body);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", content);
return await resp.Content.ReadAsStringAsync();
}
}
import json, requests
fields = [
{"type": "contactEmail", "value": "jane@example.com"},
{"type": "firstName", "value": "Jane"},
{"type": "lastName", "value": "Doe"},
{"type": "paymentAmount", "value": "50.00"},
{"type": "paymentNote", "value": "General donation"},
{"type": "cardNumber", "value": "4111111111111111"},
{"type": "cardExpMonth", "value": "12"},
{"type": "cardExpYear", "value": "2028"},
{"type": "cvv", "value": "123"},
{"type": "cardAddressZip","value": "10001"},
]
body = {
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": json.dumps(fields), # inner array must be a JSON-encoded string
}
r = requests.post(
"https://your-organization.chabadms.com/api/Forms/AddSubmissionApi",
json=body,
headers={"Accept": "application/json"},
timeout=30,
)
r.raise_for_status()
print(r.json())
const fields = [
{ type: "contactEmail", value: "jane@example.com" },
{ type: "firstName", value: "Jane" },
{ type: "lastName", value: "Doe" },
{ type: "paymentAmount", value: "50.00" },
{ type: "paymentNote", value: "General donation" },
{ type: "cardNumber", value: "4111111111111111" },
{ type: "cardExpMonth", value: "12" },
{ type: "cardExpYear", value: "2028" },
{ type: "cvv", value: "123" },
{ type: "cardAddressZip", value: "10001" },
];
const body = {
FormId: "00000000-0000-0000-0000-000000000000",
ApiToken: "YOUR_FORM_API_TOKEN",
Fields: JSON.stringify(fields), // inner array must be a JSON-encoded string
};
const resp = await fetch("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify(body),
});
const data = await resp.json();
console.log(data);
<?php
$fields = [
["type" => "contactEmail", "value" => "jane@example.com"],
["type" => "firstName", "value" => "Jane"],
["type" => "lastName", "value" => "Doe"],
["type" => "paymentAmount", "value" => "50.00"],
["type" => "paymentNote", "value" => "General donation"],
["type" => "cardNumber", "value" => "4111111111111111"],
["type" => "cardExpMonth", "value" => "12"],
["type" => "cardExpYear", "value" => "2028"],
["type" => "cvv", "value" => "123"],
["type" => "cardAddressZip","value" => "10001"],
];
$body = [
"FormId" => "00000000-0000-0000-0000-000000000000",
"ApiToken" => "YOUR_FORM_API_TOKEN",
"Fields" => json_encode($fields), // inner array must be a JSON-encoded string
];
$ch = curl_init("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json", "Accept: application/json"],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$resp = curl_exec($ch);
curl_close($ch);
echo $resp;
?>
Mode 2: Already paid
Use this mode when payment was collected outside CMS (your own payment processor, a POS terminal, cash, a check that has cleared, etc.) and you want CMS to record a paid bill so the donation appears in CMS reporting.
To use this mode: include Transaction with every element's IsCharged set to true. Do not send card fields. Embed any external reference data in ChargeResponse (or in Note) so it appears on the resulting record in CMS.
Sample request
curl -X POST "https://your-organization.chabadms.com/api/Forms/AddSubmissionApi" \
-H "Content-Type: application/json" \
-d '{
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": "[{\"type\":\"contactEmail\",\"value\":\"jane@example.com\"},{\"type\":\"firstName\",\"value\":\"Jane\"},{\"type\":\"lastName\",\"value\":\"Doe\"}]",
"Transaction": "[{\"IsCharged\":true,\"Amount\":50.00,\"Note\":\"Stripe ch_3ABC123\",\"Subcategory\":123,\"ChargeDate\":\"2026-05-10T14:32:00Z\",\"BillPeriod\":\"Once\",\"ChargeResponse\":{\"AuthorizeTransId\":\"ch_3ABC123\",\"ResponseCode\":\"Approved\",\"Success\":true}}]"
}'
var fields = new[]
{
new { type = "contactEmail", value = "jane@example.com" },
new { type = "firstName", value = "Jane" },
new { type = "lastName", value = "Doe" },
};
var transactions = new[]
{
new
{
IsCharged = true,
Amount = 50.00m,
Note = "Stripe ch_3ABC123",
Subcategory = 123,
ChargeDate = "2026-05-10T14:32:00Z",
BillPeriod = "Once",
ChargeResponse = new
{
AuthorizeTransId = "ch_3ABC123",
ResponseCode = "Approved",
Success = true
}
}
};
var body = new
{
FormId = "00000000-0000-0000-0000-000000000000",
ApiToken = "YOUR_FORM_API_TOKEN",
Fields = JsonConvert.SerializeObject(fields),
Transaction = JsonConvert.SerializeObject(transactions)
};
var json = JsonConvert.SerializeObject(body);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
import json, requests
fields = [
{"type": "contactEmail", "value": "jane@example.com"},
{"type": "firstName", "value": "Jane"},
{"type": "lastName", "value": "Doe"},
]
transactions = [{
"IsCharged": True,
"Amount": 50.00,
"Note": "Stripe ch_3ABC123",
"Subcategory": 123,
"ChargeDate": "2026-05-10T14:32:00Z",
"BillPeriod": "Once",
"ChargeResponse": {
"AuthorizeTransId": "ch_3ABC123",
"ResponseCode": "Approved",
"Success": True,
},
}]
body = {
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": json.dumps(fields),
"Transaction": json.dumps(transactions),
}
r = requests.post("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", json=body, timeout=30)
print(r.status_code, r.json())
const fields = [
{ type: "contactEmail", value: "jane@example.com" },
{ type: "firstName", value: "Jane" },
{ type: "lastName", value: "Doe" },
];
const transactions = [{
IsCharged: true,
Amount: 50.00,
Note: "Stripe ch_3ABC123",
Subcategory: 123,
ChargeDate: "2026-05-10T14:32:00Z",
BillPeriod: "Once",
ChargeResponse: {
AuthorizeTransId: "ch_3ABC123",
ResponseCode: "Approved",
Success: true,
},
}];
const body = {
FormId: "00000000-0000-0000-0000-000000000000",
ApiToken: "YOUR_FORM_API_TOKEN",
Fields: JSON.stringify(fields),
Transaction: JSON.stringify(transactions),
};
const resp = await fetch("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
console.log(await resp.json());
<?php
$fields = [
["type" => "contactEmail", "value" => "jane@example.com"],
["type" => "firstName", "value" => "Jane"],
["type" => "lastName", "value" => "Doe"],
];
$transactions = [[
"IsCharged" => true,
"Amount" => 50.00,
"Note" => "Stripe ch_3ABC123",
"Subcategory" => 123,
"ChargeDate" => "2026-05-10T14:32:00Z",
"BillPeriod" => "Once",
"ChargeResponse" => [
"AuthorizeTransId" => "ch_3ABC123",
"ResponseCode" => "Approved",
"Success" => true,
],
]];
$body = [
"FormId" => "00000000-0000-0000-0000-000000000000",
"ApiToken" => "YOUR_FORM_API_TOKEN",
"Fields" => json_encode($fields),
"Transaction" => json_encode($transactions),
];
$ch = curl_init("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($ch);
curl_close($ch);
?>
Mode 3: Unpaid pledge
Use this mode for pledge / promise-to-give workflows: record the donation intent now and collect payment later (manually, by emailing the donor a payment link, by reconciling against an incoming check, etc.). CMS records an open bill with no payment attached.
To use this mode: include paymentAmount in Fields, omit all card fields (no cardNumber, no tokenized card fields), and either omit Transaction entirely or send it with IsCharged: false.
The resulting bill appears in CMS as outstanding until a future payment (made through CMS, or imported via Mode 2) settles it.
Sample request
curl -X POST "https://your-organization.chabadms.com/api/Forms/AddSubmissionApi" \
-H "Content-Type: application/json" \
-d '{
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": "[{\"type\":\"contactEmail\",\"value\":\"jane@example.com\"},{\"type\":\"firstName\",\"value\":\"Jane\"},{\"type\":\"lastName\",\"value\":\"Doe\"},{\"type\":\"paymentAmount\",\"value\":\"50.00\"},{\"type\":\"paymentNote\",\"value\":\"Pledge for spring campaign\"}]"
}'
var fields = new[]
{
new { type = "contactEmail", value = "jane@example.com" },
new { type = "firstName", value = "Jane" },
new { type = "lastName", value = "Doe" },
new { type = "paymentAmount", value = "50.00" },
new { type = "paymentNote", value = "Pledge for spring campaign" },
};
var body = new
{
FormId = "00000000-0000-0000-0000-000000000000",
ApiToken = "YOUR_FORM_API_TOKEN",
Fields = JsonConvert.SerializeObject(fields)
};
var json = JsonConvert.SerializeObject(body);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var resp = await _http.PostAsync("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());
import json, requests
fields = [
{"type": "contactEmail", "value": "jane@example.com"},
{"type": "firstName", "value": "Jane"},
{"type": "lastName", "value": "Doe"},
{"type": "paymentAmount", "value": "50.00"},
{"type": "paymentNote", "value": "Pledge for spring campaign"},
]
body = {
"FormId": "00000000-0000-0000-0000-000000000000",
"ApiToken": "YOUR_FORM_API_TOKEN",
"Fields": json.dumps(fields),
}
r = requests.post("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", json=body, timeout=30)
print(r.json())
const fields = [
{ type: "contactEmail", value: "jane@example.com" },
{ type: "firstName", value: "Jane" },
{ type: "lastName", value: "Doe" },
{ type: "paymentAmount", value: "50.00" },
{ type: "paymentNote", value: "Pledge for spring campaign" },
];
const body = {
FormId: "00000000-0000-0000-0000-000000000000",
ApiToken: "YOUR_FORM_API_TOKEN",
Fields: JSON.stringify(fields),
};
const resp = await fetch("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
console.log(await resp.json());
<?php
$fields = [
["type" => "contactEmail", "value" => "jane@example.com"],
["type" => "firstName", "value" => "Jane"],
["type" => "lastName", "value" => "Doe"],
["type" => "paymentAmount", "value" => "50.00"],
["type" => "paymentNote", "value" => "Pledge for spring campaign"],
];
$body = [
"FormId" => "00000000-0000-0000-0000-000000000000",
"ApiToken" => "YOUR_FORM_API_TOKEN",
"Fields" => json_encode($fields),
];
$ch = curl_init("https://your-organization.chabadms.com/api/Forms/AddSubmissionApi");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($ch);
curl_close($ch);
?>
Tokenization & PCI
Sending raw card numbers and CVVs to this endpoint places your system in PCI scope. Where possible, collect the card client-side with your form's merchant-processor SDK, and send the resulting token instead of the raw card fields:
| Processor |
Fields to send instead of the raw card |
| Authorize.Net (Accept.js) |
opaqueDataDescriptor and opaqueDataValue |
| Payrix |
payrixToken |
When tokenized fields are present, omit cardNumber, cardExpMonth, cardExpYear, and cvv.
To charge a card already saved in CMS for the donor, omit all card fields and set PayId on the outer body to the saved payment-profile ID.
Recurring billing
To schedule a recurring donation, send Transaction with BillPeriod set to the desired schedule and NumOfPayments for the count:
{
"Transaction": "[{\"IsCharged\":false,\"Amount\":25.00,\"BillPeriod\":\"Monthly\",\"NumOfPayments\":12,\"Subcategory\":123}]"
}
Valid BillPeriod values: Once, Weekly, Monthly, Quarterly, Yearly. CMS records the initial bill and the recurring schedule; subsequent charges are run automatically by CMS, using the card or saved payment profile captured on the first submission.
Idempotency & retries
The endpoint is not idempotent. If a request times out and you retry, you may create a duplicate submission — and for Mode 1, you may charge the card a second time. Build deduplication on your side.
- Generate your own unique reference (an order number, UUID, or external transaction ID) per logical submission. Send it in
paymentNote (inside Fields) or, for Mode 2, in Transaction[].Note or in Transaction[].ChargeResponse.
- If a request times out or fails with a network error, check in CMS (or with your CMS administrator) before retrying.
- Do not retry on HTTP
400 — the same body will fail the same way.
Changelog
| Date |
Change |
| 2026-05-10 |
Initial document. Covers the three submission modes against POST /api/Forms/AddSubmissionApi. |