Create a Case by Consent
Create consent arbitration cases programmatically with
POST /api/v1/consent-cases.
A case created with a production key lands in the Ready to Sign
state; you then sign and pay through your dashboard. The sample
below attaches a small contract as contract_files.
For a file of 8 MiB or larger, POST /api/v1/uploads,
PUT the bytes, and send contract_files_tickets
instead (or supporting_documents_tickets
for other files). That final request stays multipart. These names
are plural; a contract-clause case uses
contract_file_ticket.
To file a contract-clause arbitration case instead, see
Create a Case by Contract.
Consent case ids start with creq_.
A test key (dvarb_test_,
created at Settings › Test API Keys)
records the signature and a $0 payment itself: the case comes
back paid,
sign_url is null, and no
email is sent. The respondent's test key can read the case as
soon as it exists and finishes it with
POST /api/v1/consent-cases/{id}/respondent,
which claims, accepts, and signs in one call; calling it again
returns the same object.
fully_executed is the final
status. A consent case does not become a
/api/v1/cases case, so
there is no arbitration case id.
See the Consent API respond in seconds
Run a schema-accurate demo and inspect the JSON immediately—no sign-in or API key required. Each response includes a unique URL where you can visit the generated consent case, but nothing is stored, emailed, uploaded, or added to an account.
import os
import uuid
import requests
response = requests.post(
"https://www.decisionlayer.ai/api/v1/consent-cases",
headers={
"Authorization": (
"Bearer "
+ os.environ["DECISIONLAYER_API_KEY"]
),
"Idempotency-Key": str(uuid.uuid4()),
},
data={
"question_for_arbitration": (
"Should the respondent return the $3,500 deposit?"
),
"financial_demand_usd": "3500.00",
"other_relief": "Return of any project files.",
"respondent_first_name": "Jordan",
"respondent_last_name": "Chen",
"respondent_email": (
"api-playground-respondent@decisionlayer.ai"
),
},
files={
"contract_files": (
"sample_contract.txt",
b"Sample contract for the consent demo.",
"text/plain",
)
},
timeout=60,
)
response.raise_for_status()
print(response.json())
{
"message": "Press Run to generate a demo consent response.",
"case": null
}
The Run button uses a safe demo endpoint. It never writes to the database, uploads a file, sends email or Slack notifications, collects a signature, or files a case. Copy the Python example when you are ready to call the real API with your key.
-
1
Create an API key at /settings/api-keys and copy its full value (shown only once).
-
2
Install the only dependency:
pip install requests -
3
Paste your key into the script below (replace
PASTE_YOUR_KEY_HERE) and runpython create_consent_case.py.
Authenticate every request with a bearer token:
Authorization: Bearer dvarb_your_key_here
The script sends a fresh
Idempotency-Key.
A failed upload releases the key when no consent case remains.
A draft that survives replays the stored
500.
create_consent_case.py
"""DecisionLayer API example: create a consent arbitration case.
Consent cases live at /api/v1/consent-cases. To file a contract-clause
arbitration case instead, see /api/create-a-case (POST /api/v1/cases).
Change API_KEY and RESPONDENT_EMAIL below. Create a key at
https://www.decisionlayer.ai/settings/api-keys
A production key (dvarb_...) returns status ready_to_sign and a sign_url.
After you sign and pay on the web, the respondent is emailed an invitation.
A test key (dvarb_test_...) records the signature and a $0 payment for you:
the case comes back paid, sign_url is null, and no email is sent. The
respondent's test key then finishes it with one call,
POST /api/v1/consent-cases/{id}/respondent. fully_executed is the final
status; a consent case does not become a /api/v1/cases case.
Then run:
pip install requests
python create_consent_case.py
"""
import uuid
import requests
# 1. Paste your API key here (from /settings/api-keys).
API_KEY = "PASTE_YOUR_KEY_HERE"
# 2. Base URL of the DecisionLayer API. Use http://localhost:8000 locally.
BASE_URL = "https://www.decisionlayer.ai"
# 3. The respondent's email. A production case invites this address after
# you pay, so use a real one. With test keys, use the email of the account
# that owns the respondent's test key.
RESPONDENT_EMAIL = "jordan.chen@example.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def main() -> None:
# Create a small sample attachment so the demo includes a contract file.
with open("sample_contract.txt", "w") as handle:
handle.write("Sample contract for the DecisionLayer consent demo.\n")
# The fields below mirror the consent web form. Claimant details default
# to the API key owner's account when omitted.
#
# This sample is the small-file path (under 8 MiB): repeatable multipart
# fields contract_files and supporting_documents. For a larger file,
# POST /api/v1/uploads, PUT the bytes with the returned Content-Type and
# Content-Length, then send contract_files_tickets or
# supporting_documents_tickets in this same multipart request. Those
# names are plural. A contract-clause case uses contract_file_ticket.
#
# Idempotency-Key is scoped to this API key. A fresh key files a new
# case. Reuse one only to retry this body. A failed upload releases the
# key when no consent case remains. A draft that survives replays the
# stored 500.
data = {
"question_for_arbitration": "Respondent kept a $3,500 deposit.",
"financial_demand_usd": "3500.00",
"other_relief": "Return of any project files.",
"respondent_first_name": "Jordan",
"respondent_last_name": "Chen",
"respondent_email": RESPONDENT_EMAIL,
}
with open("sample_contract.txt", "rb") as contract:
files = {
"contract_files": ("sample_contract.txt", contract, "text/plain"),
}
response = requests.post(
f"{BASE_URL}/api/v1/consent-cases",
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
data=data,
files=files,
timeout=60,
)
if response.status_code != 201:
print(f"Request failed ({response.status_code}):")
print(response.json())
raise SystemExit(1)
result = response.json()
case = result["case"]
# Consent case ids start with creq_, not case_.
print(f"Created consent case: {case['id']}")
print(f"Status: {case['status']}")
if result["sign_url"]:
print(f"Sign it here: {result['sign_url']}")
else:
print("Test filing: already signed and paid. Nothing to do on the web.")
print(
"The respondent's test key finishes it with "
f"POST /api/v1/consent-cases/{case['id']}/respondent"
)
# List your cases to confirm it shows up.
listing = requests.get(
f"{BASE_URL}/api/v1/consent-cases", headers=HEADERS, timeout=60
)
listing.raise_for_status()
print(f"You now have {len(listing.json())} consent case(s).")
if __name__ == "__main__":
main()
Understanding errors
Every error returns a consistent JSON shape with a list of
actionable details:
{
"error": {
"status": 422,
"message": "Your request could not be processed.",
"details": [
"Field 'respondent_email' is required. Provide the respondent's email address, e.g. respondent_email='jordan@example.com'."
]
}
}