CloudPay's API (CloudPayClover) serves the CloudPay web app; it is not a public API. Its routes live under /api/v1 on the API's own host, except /health. The web app has its own same-origin route handlers under /api/*, listed in their own group. The last group is what the API calls at Clover.
Two environments, development and production, each with its own web container app, API App Service, database and Clerk instance. Development uses Clover's sandbox; production uses Clover's production hosts.
| Section | Keys and effect |
|---|---|
ConnectionStrings | DefaultConnection (SQL Server). Redis is optional; when it is empty the cache is in memory. |
Clover | ClientId, ClientSecret, Environment (sandbox, production or eu, which picks Clover's hosts), WebhookSecret, RemoteApplicationId |
Clerk | SecretKey (Clerk's backend API), Authority (the issuer whose tokens the API accepts), AuthorizedParties (allowed azp values), WebhookSigningSecret |
Jwt | Secret, Issuer and Audience (both default to CloudPayClover), ExpirationInMinutes (60), for the API's own JWT |
KeyVault:VaultUri | When set, secrets such as the Twilio and SMTP2GO credentials are read from Key Vault with DefaultAzureCredential. When empty, they come from the Secrets section. |
Smtp2Go | Host, port (2525), TLS, and the sender address and name. The username and password are secret names. |
Twilio | The sender number, the names of the account SID and auth-token secrets, and an optional StatusCallbackUrl |
Contact | SalesInbox for demo requests, Enabled, and the per-IP rate limit (RateLimitPermit 5 per RateLimitWindowMinutes 10) |
Cors:AllowedOrigins | The web app's origins. Empty means any origin, without credentials. |
| Variable | Used for |
|---|---|
NEXT_PUBLIC_BACKEND_URL | The API's base URL |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk in the browser, with the NEXT_PUBLIC_CLERK_SIGN_IN_URL, …SIGN_UP_URL and fallback-redirect variables |
NEXT_PUBLIC_TARGET_CLOVER_DOMAIN | The Clover host for the device SDK and the transaction route handlers |
NEXT_PUBLIC_REMOTE_APP_ID | The Clover remote app id the SDK connects as |
NEXT_PUBLIC_CLARITY_PROJECT_ID | Turns Clarity on in production builds |
CLERK_SECRET_KEY, CLERK_WEBHOOK_SECRET | Clerk on the server, and the Clerk webhook route |
DMS_API_URL, DMS_CLIENT_ID, DMS_CLIENT_SECRET | The DMS service, from the server only |
The five NEXT_PUBLIC_* values are baked into the image when it is built, from the pipeline's variable group for that environment. Server-only values are set on the container app. src/web/.env.example is out of date: it lists the Clover domain and app id without the NEXT_PUBLIC_ prefix and leaves out several server variables.
Authorization: Bearer …. The API reads the token's issuer: a Clerk session token goes to the Clerk scheme, anything else to the API's own JWT scheme.azp claim against Clerk:AuthorizedParties. The API then looks the user up (cached for 60 seconds), creating the local user from Clerk the first time, and adds role, merchant_id and clover_merchant_id. Any role inside the token is discarded. Users of a deactivated merchant get no merchant claims, so merchant routes refuse them.POST /api/v1/auth/session. Its sub is the merchant's id, alongside clover_merchant_id, role and user_provider_id. It is the older path, kept while the web app moves to Clerk tokens; today the web app uses it for merchant routes and Clerk tokens for the session and admin routes.PlatformAdmin and AdminOnly need role = platform_admin; MerchantAdmin accepts platform_admin or merchant_admin. Routes marked Signed-in user need only a valid token.{timestamp}.{body} and Clover:WebhookSecret; Clerk's with Svix and Clerk:WebhookSigningSecret.{ error, statusCode }. A broken Clover connection adds tokenStatus: "Disconnected" and requiresReconnect: true (401); a deactivated merchant adds tokenStatus: "Deactivated" and isDeactivated: true (403). Domain and validation errors are 400, a missing merchant 404, anything else 500 with a generic message.Z, whatever the database returns.page (from 1) and pageSize (default 20) return { items, totalCount, page, pageSize, totalPages, hasNextPage, hasPreviousPage }. The invoice list uses OData instead: $filter, $orderby, $select, $count, $skip and $top (up to 100), 50 rows by default, with the total in @odata.count.status is a name (Captured, Refunded…); transactionType and result are Clover's uppercase strings (AUTH, REFUND, APPROVED…); an invoice's status is a number (see Product Overview).| Service | Call | Purpose |
|---|---|---|
| Clover OAuth | oauth/v2/authorize, oauth/v2/token, oauth/v2/refresh, and the v1 oauth/token exchange as a fallback for first installs | Connect a merchant and keep its tokens fresh (API) |
| Clover REST v3 | Merchant, payment, order and refund reads (last group) | Webhook processing, receipts, the logo and time zone (API) |
| Clover REST v3 | Payments, refunds, orders, devices, items, and the merchant's name and time zone | Live transaction lists, devices, inventory and settings sync (web route handlers) |
| Clover Hosted Checkout | invoicingcheckoutservice/v1/checkouts | Invoice payment pages (API) |
| Clover Cloud Pay Display | remote-pay-cloud over a WebSocket | Sales, refunds and receipt prompts on the terminal (browser) |
| Clerk Backend API | Users, user metadata, invitations | Sign-in context, roles and invitations (API) |
| DMS service | POST api/v1/auth/token with client credentials, then searches, lookups and settings | Repair orders, parts invoices, Reynolds settings (web route handlers) |
| Twilio | Messages | Invoice and receipt texts (API) |
| SMTP2GO | SMTP | Invoice, receipt, paid-notification and demo-request emails (API) |
The API retries transient failures from Clover and Clerk 3 times with exponential back-off. The DMS client caches its token until a minute before expiry and retries once after a 401.
Grouped by audience. Paths are relative to the API base URL; {id}-style segments are GUIDs unless noted.
Base /api/v1.
| Endpoint | Auth | Notes |
|---|---|---|
POST | Signed-in user | Clerk session token only. Body { cloverMerchantId }, used when the Clerk account has none. Checks that the merchant exists and its Clover connection is live, creates or updates the local user, and returns { token, role, merchant, user, connectionStatus }. 400 with a tokenStatus when Clover needs reconnecting; 404 for an unknown merchant. |
GET | Anonymous | ?code_challenge=&state=&redirect_uri=. Returns { authorizationUrl } for Clover OAuth v2 with PKCE (S256). |
POST | Anonymous | { code, codeVerifier, redirectUri, merchantId }. Exchanges the code with Clover, finds or creates the merchant (by Clover id, then by merchantId for the admin flow), stores the encrypted tokens, and syncs the name and time zone. Returns { token, merchantId, expiresIn }. |
POST | Signed-in user | Refreshes the caller's merchant's Clover tokens. The web app does not use it. |
POST | Signed-in user | Marks the caller's merchant's Clover tokens revoked. 204. |
Base /api/v1.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Signed-in user | The merchant: id, Clover id, business name, email, active flag, created date and settings. |
PUT | Signed-in user | { businessName?, email?, isActive?, settings? }. The logo, and any display groups left out, are kept. |
POST | Signed-in user | Copies the merchant's logo from Clover when it has changed. Returns { logoUrl }. |
GET | Signed-in user | { isConnected, tokenStatus, accessTokenExpiresAt, lastRefreshedAt } |
GET | Signed-in user | { accessToken, merchantId, expiresAt } (epoch ms): the merchant's Clover token, which the browser needs for the device SDK and the transaction route handlers. |
GET | Merchant admin | The merchant's users. page, pageSize, search, role, department, sortBy (name, email, role, department, createdAt), sortDescending. |
PUT | Merchant admin | { role: merchant_admin | merchant_user, department?: Parts | Service }. Updates Clerk too. Refuses your own role and platform admins. |
GET | Signed-in user | The caller's { theme, notificationsEnabled, timeZone, favoriteDevice, notifications }. Defaults for a user not stored yet. |
PUT | Signed-in user | Replaces all of them. 400 for an unknown time zone or a favourite device without an id. |
Base /api/v1/payments. These read the API's copy of payments, written by Clover's webhooks.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Signed-in user | page, pageSize, status (Pending, Authorized, Captured, Voided, Refunded, Failed), startDate, endDate. Newest first. |
GET | Signed-in user | One payment. 404 when the API has no copy of it. |
GET | Signed-in user | ?externalReferenceId=: every payment and refund carrying that reference (an RO or invoice number), newest first. |
POST | Signed-in user | { externalReferenceIds: [...] }, up to 100. Returns { reference: [payments] }, leaving out references with none. |
GET | Signed-in user | startDate, endDate. Totals in cents (amount, tips, tax), counts (all, captured, failed), and the refund count and total. |
POST | Signed-in user | { note }, required, up to 1,000 characters. Creates or replaces the reason for that payment's refund or void. |
GET | Signed-in user | { sourceCloverPaymentId, note, createdByUserId, createdAt }, or 404. |
GET | Signed-in user | The ids of every payment with a saved reason, so lists can flag them in one request. |
Base /api/v1.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Signed-in user | OData list (see Conventions), e.g. $filter=Status eq 0&$orderby=Created desc&$count=true&$top=25. |
GET | Signed-in user | startDate, endDate, status. Outstanding and paid totals, counts per status, average days to payment, and the overdue count (outstanding for more than 30 days). One SQL statement. |
GET | Signed-in user | One invoice. |
POST | Signed-in user | { amount, taxAmount, tipAmount, totalAmount, sendToEmail, sendToSMS, orderId?, dmsSnapshot?, … }. Needs a valid email or phone. Saves the invoice as Outstanding and sends it. 201. |
POST | Signed-in user | Refuses a paid invoice; cancelling twice is harmless. |
POST | Signed-in user | { invoiceId, sendEmail?, sendSMS? }. Outstanding invoices only. New contacts replace the old ones. |
POST | Signed-in user | { cloverPaymentId | cloverRefundId, sendEmail?, sendSMS? }. Fetches the payment or refund from Clover and sends the receipt. Returns { cloverPaymentId, emailSent, smsSent, sentAt }, and fails only when every requested channel failed. |
Base /api/v1/admin. Platform admins only.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Platform admin | page, pageSize, search (name or email), isActive, isConnected, sortBy (businessName, email, createdAt, isActive), sortDescending. |
GET | Platform admin | Detail, with the settings and the connection's last refresh. |
POST | Platform admin | { businessName, email?, cloverMerchantId?, settings? }. Invites the email as merchant admin. |
PUT | Platform admin | { businessName?, email?, isActive?, settings? } |
PATCH | Platform admin | { isActive }. 204. |
POST | Platform admin | An authorization URL with the merchant's id as state. The web app builds its own PKCE request instead. |
POST | Platform admin | Marks the merchant's Clover tokens revoked. 204. |
GET | Platform admin | Same filters as the merchant's own users list. |
PUT | Platform admin | Same rules as the merchant's own version. |
GET | Platform admin | ?query= (2 or more characters): up to 10 Clerk users. |
POST | Platform admin | { userId, cloverMerchantId }: writes the Clover merchant id to the Clerk user. |
POST | Platform admin | { userId }: removes it. |
POST | Platform admin | { userId, role }, any of the three roles. The web app does not use it. |
POST | Platform admin | { emailAddress, userRole: merchant_admin | merchant_user }. Needs a connected merchant. Clerk emails the invitation. |
GET | Platform admin | Pending invitations. |
DELETE | Platform admin | Revokes it in Clerk. 204. |
GET | Platform admin | Encrypts and decrypts a probe with the token key ring: { healthy, keysPersisted, ciphertextLength }. |
No sign-in.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Anonymous | The invoice pay link. Redirects to Clover Hosted Checkout, or returns an HTML page for a paid (200), unavailable (404) or failing (500) invoice. |
POST | Anonymous | { name, email, company, phone?, message, website }, where website is the honeypot. 202; 400 on validation; 429 over the rate limit; 503 when the email could not be sent. |
POST | Signed webhook | Clover's events. A body with verificationCode is Clover's setup check and is only logged. Otherwise each P: (payment) or R: (refund) object is fetched from Clover and upserted, and failures are retried in the background. Answers 200 once the body is accepted. |
POST | Signed webhook | On user.created, applies the newest pending invitation for the user's email. |
POST | Anonymous | Twilio's delivery-status callback. Logged only. |
GET | Anonymous | 200 while the process is up. No dependency checks. |
Same-origin /api/* on the web app's host. Clover OAuth below means the caller passes the merchant's Clover token, from GET /api/v1/merchant/token, in a header or the body.
| Endpoint | Auth | Notes |
|---|---|---|
GET | Clover OAuth | Clover's latest payments, with voids and refunds. Feeds Today's Transactions. |
GET | Clover OAuth | Payments, voids and refunds merged, sorted and paged. status, type, dates, and all=true for the CSV export. Defaults to the last 90 days. |
GET | Clover OAuth | Refunds for a date range, with a limit. |
GET | Clover OAuth | ?paymentId=: every payment and refund on that payment's Clover order. |
GET | Clover OAuth | Items with stock and categories; search, sort and paging. |
POST | Clover OAuth | The merchant's devices, each one's last payment, and the merchant's address. |
POST | Clover OAuth | Reads the business name and time zone from Clover. PUT on the same path writes them. |
POST | Signed-in user | RO search through the DMS service. The merchant comes from the Clerk session. /lookup fetches one RO. |
POST | Signed-in user | Parts-invoice search. /lookup fetches one. |
GET | Platform admin | ?merchantId=: the merchant's Reynolds settings, or null when it is not provisioned. POST saves them. |
POST | Signed webhook | Svix-verified. Logs user.created; the API's webhook does the work. |
POST | Anonymous | Content-Security-Policy violation reports, written to the log. 204. |
Called by the API on Clover's API host for the configured environment. REST reads use the merchant's OAuth token.
| Endpoint | Auth | Notes |
|---|---|---|
GETv3 | Clover OAuth | expand=cardTransaction,refunds,order. Webhook upserts (refunds arrive nested in the payment) and receipts. |
GETv3 | Clover OAuth | expand=payment.cardTransaction. Refund webhooks and refund receipts. |
GETv3 | Clover OAuth | expand=lineItems: the notes that carry an invoice id. |
GETv3 | Clover OAuth | expand=properties,logos: the name, time zone and logo. |
POSTinvoicingcheckoutservice | Clover OAuth | Header X-Clover-Merchant-Id. One line item for the invoice total, the invoice id in the cart and line-item notes, and the customer's email and phone. Returns href, checkoutSessionId and an expiry. |
POSToauth | Client credentials | { client_id, client_secret, code }. The merchant id comes from the response or the token's merchant_uuid. For first installs from the App Market it falls back to the v1 exchange, then migrates with grant_type: authorization_code_legacy. |
POSToauth | Client credentials | Refreshes a merchant's tokens with its refresh token. |