Forms Submissions API

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:

  1. Charge now — you send the donor's card details; CMS processes the payment and records a paid bill.
  2. 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.
  3. 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.

  1. In CMS, open the form's settings page and copy the form's API token.
  2. In your code, include that token as the ApiToken field on every request body.
  3. 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:

  1. If HTTP status is 200 and the JSON body's StatusCode == "OK" and Errors is empty — treat as success.
  2. 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.
  3. 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.