Payment
Takes money. Use this whenever the request mentions a price, a fee, a ticket, a deposit or a UPI id, and never a text question asking them to confirm they paid. When the author has a payment account connected, a payment with no link or UPI id is taken through verified checkout on it, so give the amount and leave the link out unless one was named.
paymentTakes money. Use this whenever the request mentions a price, a fee, a ticket, a deposit or a UPI id, and never a text question asking them to confirm they paid. When the author has a payment account connected, a payment with no link or UPI id is taken through verified checkout on it, so give the amount and leave the link out unless one was named.
How it gets answered — arrives out of band (an upload, a payment, or a booking).
Configuration
Fields specific to payment. The fields every block has — id, ref, title, required, visibility, media, agentHints, prefillParam, identityField — are documented once.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"buttonLabel": {
"type": "string",
"maxLength": 60
},
"type": {
"type": "string",
"const": "payment"
},
"method": {
"default": "link",
"type": "string",
"enum": [
"link",
"upi",
"gateway"
]
},
"amountMode": {
"default": "fixed",
"type": "string",
"enum": [
"fixed",
"variable",
"answer"
]
},
"amount": {
"type": "number",
"minimum": 0
},
"priceFrom": {
"type": "object",
"properties": {
"ref": {
"type": "string",
"maxLength": 120
},
"prices": {
"type": "object",
"propertyNames": {
"type": "string",
"maxLength": 64
},
"additionalProperties": {
"type": "number",
"minimum": 0,
"maximum": 10000000
}
}
},
"required": [
"ref",
"prices"
]
},
"quantityFrom": {
"type": "object",
"properties": {
"ref": {
"type": "string",
"maxLength": 120
}
},
"required": [
"ref"
]
},
"amountVariable": {
"type": "string"
},
"minAmount": {
"type": "number",
"minimum": 0
},
"maxAmount": {
"type": "number",
"minimum": 0
},
"currency": {
"default": "USD",
"type": "string",
"minLength": 3,
"maxLength": 3
},
"paymentAccountId": {
"type": "string",
"maxLength": 40
},
"url": {
"type": "string",
"maxLength": 500,
"format": "uri"
},
"upiId": {
"type": "string",
"maxLength": 120
},
"upiPayeeName": {
"type": "string",
"maxLength": 120
}
},
"required": [
"id",
"ref",
"title",
"type"
],
"$defs": {
"__schema0": {
"type": "object",
"properties": {
"op": {
"type": "string",
"enum": [
"and",
"or"
]
},
"conditions": {
"default": [],
"type": "array",
"items": {
"type": "object",
"properties": {
"left": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "ref"
},
"ref": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{1,40}$"
}
},
"required": [
"kind",
"ref"
]
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "variable"
},
"name": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,40}$"
}
},
"required": [
"kind",
"name"
]
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "hidden"
},
"name": {
"type": "string",
"pattern": "^[a-zA-Z_][a-zA-Z0-9_.-]{0,60}$"
}
},
"required": [
"kind",
"name"
]
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "literal"
}
},
"required": [
"kind"
]
}
]
},
"op": {
"type": "string",
"enum": [
"eq",
"neq",
"gt",
"gte",
"lt",
"lte",
"contains",
"not_contains",
"starts_with",
"ends_with",
"matches_regex",
"is_empty",
"is_not_empty",
"is_checked",
"is_not_checked",
"includes",
"not_includes",
"ranked_above",
"ranked_below"
]
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"required": [
"left",
"op"
]
}
},
"groups": {
"default": [],
"type": "array",
"items": {
"$ref": "#/$defs/__schema0"
}
}
},
"required": [
"op"
]
}
}
}In a generated draft these arrive as config pairs: amount=<number>, currency=<3-letter code, INR for rupees>. When the price depends on what they chose (a plan, a ticket tier, a size), put that single-choice, dropdown or yes_no question BEFORE the payment (a yes_no prices Yes and No) and write price_from=<its ref>; prices=<option label>:<amount>|<option label>:<amount>, one price for every option, and no amount=. When the price is per person, per ticket or per item ('₹1000 per guest'), put a number question counting them BEFORE the payment, set amount=<price for one>, and write quantity_from=<its ref>. Only when the author named one: method=upi with upi=<vpa like name@bank>, or method=link with url=<checkout page>.
What you receive
GET /v1/forms/{id} and every "next question" projects blocks through toPublicBlock. For the example above:
{
"id": "blk_pay00001",
"ref": "q_payment",
"type": "payment",
"title": "Pay the deposit",
"required": true,
"imageKey": null,
"media": null,
"currency": "USD",
"amount": 4900,
"paymentMethod": "link",
"url": "https://pay.example.com/x"
}What you send
On a link or upi block, the respondent's report that they paid — never proof of it. On a gateway block, nothing you send is accepted: the answer is written by chatform from the payment the form admin's own gateway confirmed, and reads back as { status: "paid", method: "gateway", verified: true, provider, paymentRecordId, paymentId, amount, currency, paidAt }.
type Answer = { status: "pending" | "paid"; method?: "link" | "upi"; reference?: string; amount?: number };curl -X POST https://api.chatform.in/v1/responses/{RESPONSE_ID}/answers \
-H "x-api-key: $CHATFORM_SECRET_KEY" \
-H "content-type: application/json" \
-d '{"ref": "q_payment", "value": {"status":"paid","method":"link","reference":"CF-7K2M9X","verified":true}}'What gets stored
The value is normalised before it is saved, so what you read back is not always what you sent.
| You send | Stored | |
|---|---|---|
{"status":"paid","method":"link","reference":"CF-7K2M9X","verified":true} | {"status":"paid","method":"link","verified":false,"reference":"CF-7K2M9X","currency":"USD"} | verified is forced to false however it arrives — a link or upi payment happens where nothing can check it, so nobody can confirm their own |
Errors
| Code | When | Message |
|---|---|---|
required | an empty answer on a required block | This question needs an answer. |
type | "paid" | Payment required. |
payment_pending | {"status":"maybe"} | Payment has not been completed yet. |
payment_unverified | {"status":"paid","method":"gateway","verified":true,"paymentId":"pay_Q1w2E3r4"} — on a gateway block, every value is refused — only a payment the gateway confirmed can answer it | Use the Pay button to complete payment. |