Payment Links & Subscriptions
Create hosted payment links for one-time charges and direct billing installment plans, or standalone subscriptions for recurring monthly charges.
Base URL:
https://payments.audit1.com/api/v1— every endpoint on this page lives there. This is a different host from the core Developer API (https://apiv2.audit1.com/api/v2, used for payroll reports, employee sync, and API keys) — that host does not serve payment endpoints, and calling it for/payment-linksreturns404.
Payment Links #
Create a Payment Link #
POST /api/v1/payment-links
| Field | Type | Required | Description |
|---|---|---|---|
employer_id |
string | Yes | Employer ObjectId |
carrier_id |
string | Conditional | Carrier ObjectId. Required unless your API key is scoped to exactly one carrier — single-carrier keys infer it server-side |
policy_id |
string | Yes | Policy ObjectId |
premium_cents |
integer | Yes | Premium amount in cents ($1,500.00 = 150000) |
fee_cents |
integer | No | Fee amount in cents (default: 0). Total charged = premium_cents + fee_cents |
fee_breakdown |
array | No | Itemized fee labels: [{ "label": "Setup fee", "amount_cents": 5000 }] |
payment_type |
string | Yes | ONE_TIME or DIRECT_BILLING |
payment_context |
string | Yes | Purpose of the payment (see table below) |
customer.name |
string | Yes | Insured business name |
customer.email |
string | Yes | Email to receive the payment link |
customer.phone |
string | No | Phone number |
delivery.email |
boolean | No | Send link via email (default: true) |
delivery.sms |
boolean | No | Send link via SMS (default: false) |
total_installments |
integer | Conditional | Number of recurring installments. Required if payment_type is DIRECT_BILLING (min: 2) |
down_payment_months |
integer | Conditional | Months of premium for down payment. Required if payment_type is DIRECT_BILLING (min: 1) |
created_by |
string | No | User or system that created the link |
after_completion_url |
string | No | URL to redirect after payment (must be a valid URL) |
invoice_id |
string | No | Associate with an existing invoice |
metadata |
object | No | Custom key-value pairs, string values only (e.g. { "invoice_number": "INV-2044" }). Stored on the link and its payment record, and returned in the payment.completed webhook's metadata field — this is the standard way to round-trip your own reference data through a payment. Your keys come back with their values untouched, but the object is not identical to the one you sent: we add api_client_id (your tenant id) and source: "payment_api" before storing it. Those two names are reserved — set either yourself and ours overwrites it, silently, so pick different names for your own data. Read the keys you set and ignore the rest; don't compare the whole object for equality |
Fees
Audit1 charges no platform fee on payment links — there is no Audit1 rate to publish. Any amount on top of premium_cents is entirely yours to set via fee_cents; Audit1 never adds to it or takes a cut of it. The customer is charged the two together as one amount: total_cents = premium_cents + fee_cents.
When fee_cents is greater than 0, Audit1 records it and flags it as fee_source: "partner" — on the link, on the payment record, and in the payment.completed webhook — so it's always traceable back to you rather than to Audit1. Use fee_breakdown to itemize what the fee is for; it's stored for your own records but doesn't change how the charge or the webhook fields work.
Payment Context Values
| Context | Description |
|---|---|
down_payment |
Initial down payment on a policy |
installment |
Recurring installment payment |
late_payment |
Late premium payment |
extra_payment |
Additional payment beyond scheduled |
paygo |
Pay-as-you-go premium collection |
overdue_payment |
Overdue balance |
premium_due |
Standard premium due |
one_time_payment |
Generic one-time charge |
full_payment |
Pay-in-full annual premium (single ONE_TIME charge) |
deposit |
Deposit / hold |
setup_fee |
Account setup fee |
minimum_payment |
Minimum payment due |
additional_payment |
Additional voluntary payment |
audit1_fee |
Audit1 platform fee |
extra_fee |
Extra fee |
extra_carrier_fee |
Carrier-specific extra fee |
bank_fee |
Bank-related fee |
audit_program_fee |
Audit program fee |
program_fee |
General program fee |
remittance |
Remittance payment |
late_registration_fee |
Late registration penalty |
late_initial_reporting_fee |
Late initial reporting penalty |
late_payroll_reporting |
Late payroll reporting penalty |
late_quarterly_941s |
Late quarterly 941s penalty |
late_state_unemployment |
Late state unemployment penalty |
late_coi_reporting |
Late COI reporting penalty |
payroll_billing_fee |
Payroll billing fee |
manual_reporting_fee |
Manual reporting fee |
Example: One-Time Payment
curl -X POST https://payments.audit1.com/api/v1/payment-links \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"employer_id": "681xyz789abc123456789012",
"carrier_id": "680abc456def789012345678",
"policy_id": "682def789ghi012345678901",
"premium_cents": 250000,
"fee_cents": 5000,
"payment_type": "ONE_TIME",
"payment_context": "down_payment",
"customer": {
"name": "Smith Industries",
"email": "ap@smithindustries.com"
}
}'
carrier_id may be omitted here — single-carrier keys infer it server-side.
Response (201 Created):
{
"ok": true,
"data": {
"_id": "683abc123def456789012345",
"stripe_payment_link_id": "plink_1abc2def3ghi...",
"stripe_url": "https://checkout.stripe.com/c/pay/cs_a1b2c3...",
"status": "created",
"billing_entity_type": "employer",
"total_cents": 255000,
"link_amount_cents": 255000,
"installment_plan": null
}
}
Example: Direct Billing with Installment Plan
curl -X POST https://payments.audit1.com/api/v1/payment-links \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"employer_id": "681xyz789abc123456789012",
"carrier_id": "680abc456def789012345678",
"policy_id": "682def789ghi012345678901",
"premium_cents": 1200000,
"fee_cents": 50000,
"payment_type": "DIRECT_BILLING",
"payment_context": "down_payment",
"total_installments": 10,
"down_payment_months": 3,
"customer": {
"name": "Smith Industries",
"email": "ap@smithindustries.com"
}
}'
carrier_id may be omitted here — single-carrier keys infer it server-side.
This creates a payment link for the calculated down payment portion. After the insured pays, a monthly subscription is automatically created for the remaining installments.
Response (201 Created):
{
"ok": true,
"data": {
"_id": "683abc123def456789012345",
"stripe_payment_link_id": "plink_1abc2def3ghi...",
"stripe_url": "https://checkout.stripe.com/c/pay/cs_a1b2c3...",
"status": "created",
"billing_entity_type": "employer",
"total_cents": 1250000,
"link_amount_cents": 312500,
"installment_plan": {
"total_installments": 10,
"down_payment_percentage": 25,
"down_payment_cents": 312500,
"per_installment_cents": 93750,
"extra_fee_cents": 0
}
}
}
In sandbox
, stripe_url won't be a checkout.stripe.com link — it points to an Audit1-hosted stub page (https://portal.audit1.com/pay/{id}) instead, since sandbox links never touch real Stripe. Use Simulate a Completed Payment below to mark it paid.
List Payment Links #
GET /api/v1/payment-links?carrier_id=...&employer_id=...&status=created&page=1&limit=50
Get a Payment Link #
GET /api/v1/payment-links/{id}
Cancel a Payment Link #
DELETE /api/v1/payment-links/{id}
Deactivates the link on Stripe and marks it as cancelled in the database. Cannot cancel a link that has already been paid (use refund instead).
Refund a Payment #
POST /api/v1/payment-links/{id}/refund
| Field | Type | Required | Description |
|---|---|---|---|
amount_cents |
integer | No | Partial refund amount in cents (omit for full refund) |
reason |
string | No | duplicate, fraudulent, or requested_by_customer |
Refunds are tracked as refunded_cents on the payment record, with individual refund entries in a refunds array. Multiple partial refunds are supported.
Simulate a Completed Payment (Sandbox) #
POST /api/v1/payment-links/{id}/simulate-completion
The fastest way to test your integration end to end: mark a sandbox payment link as paid without a real card and without waiting for anyone to click through checkout. This is not a mocked copy of an event — it drives the exact same completion path a real Stripe payment does, and fires the exact same, correctly-signed payment.completed webhook to your registered endpoints.
Sandbox keys only. Call it with a live key and you get 403 Forbidden ("Completion simulation is available for sandbox keys only"). Only links created with a sandbox key exist to be simulated in the first place, so this can never touch real money.
It takes no parameters. Send an empty body — the endpoint always simulates the link's full total_cents being paid.
There is no short-payment option today.
An earlier version of this page documented an amount_cents body for simulating a partial payment. It does not work: this endpoint forwards no request body to the service that would honour it, so amount_cents is discarded in transit and you get a full-amount completion regardless. It has never behaved otherwise. If you need to exercise a partial-outstanding-balance branch, tell us at support@audit1.com — the capability exists one layer down and only needs plumbing through.
curl -X POST https://payments.audit1.com/api/v1/payment-links/683abc123def456789012345/simulate-completion \
-H "X-Client-ID: audit1_test_cli_your_id_here" \
-H "X-Client-Secret: audit1_test_sec_your_secret_here" \
-H "Content-Type: application/json" \
-d '{}'
✓ Response
{
"ok": true,
"data": {
"_id": "683abc123def456789012345",
"status": "paid"
}
}
Calling it again on a link that's already paid is a safe no-op — you get the same response back (with already_paid: true added) instead of an error or a second webhook delivery.
Info
Today this only simulates pay-in-full (ONE_TIME) links. DIRECT_BILLING installment links aren't supported by this endpoint yet.
Register a webhook endpoint before you simulate a completion, or there's nothing listening for the event — see Payment Webhooks.
Subscriptions #
Standalone subscriptions for recurring monthly charges. Requires the employer to already have a Stripe customer (created automatically when a payment link is first created for that employer).
Create a Subscription #
POST /api/v1/subscriptions
| Field | Type | Required | Description |
|---|---|---|---|
employer_id |
string | Yes | Employer ObjectId |
carrier_id |
string | Conditional | Carrier ObjectId. Required unless your API key is scoped to exactly one carrier — single-carrier keys infer it server-side |
policy_id |
string | Yes | Policy ObjectId |
amount_cents |
integer | Yes | Amount per billing period in cents |
payment_method_id |
string | Yes | Stripe payment method ID (e.g., pm_1abc...). The employer must have a Stripe customer first |
metadata |
object | No | Custom key-value pairs |
The billing interval is always month (monthly). The payment_method_id is set as the default payment method on the Stripe customer.
List Subscriptions #
GET /api/v1/subscriptions?carrier_id=...&status=active&page=1&limit=50
Get a Subscription #
GET /api/v1/subscriptions/{id}
Cancel a Subscription #
DELETE /api/v1/subscriptions/{id}
Cancels the subscription on Stripe and marks all remaining scheduled payments as cancelled.
Payment Link Statuses #
| Status | Description |
|---|---|
created |
Payment link created, awaiting payment |
sent |
Link sent to customer (via email/SMS) |
paid |
Payment received successfully |
cancelled |
Manually cancelled |
expired |
Link expired without payment |
Refunds do not change the payment link status. Refund amounts are tracked separately via
refunded_centson the associated payment record.
Subscription Statuses #
| Status | Description |
|---|---|
active |
Billing on schedule |
past_due |
Payment failed, retrying |
canceled |
Manually canceled |
incomplete |
Initial payment failed or pending |
Installment Plan Fields #
When payment_type is DIRECT_BILLING, the response includes an installment_plan object:
| Field | Type | Description |
|---|---|---|
total_installments |
integer | Number of recurring installments |
down_payment_percentage |
number | Percentage of total used as down payment |
down_payment_cents |
integer | Calculated down payment amount in cents |
per_installment_cents |
integer | Amount per installment in cents |
extra_fee_cents |
integer | Additional fee applied to installments |
The link_amount_cents in the response is the down payment amount (what the initial link charges). The remaining balance is collected via the auto-created subscription.
All monetary values are in cents. $1,500.00 =
150000. This avoids floating-point precision issues.