Best Practices Guide
This guide covers recommended patterns and practices for building a robust, secure, and maintainable integration with the Procare Pay Scheduled Payments API.
Authentication
Obtaining a Token
All endpoints require a Cognito OAuth2 bearer token obtained via the client credentials flow. Pass it in every request as:
Authorization: Bearer <your-token>
The required OAuth2 scope is:
https://scheduled-payments.<env>-procarepay.com/api.execute
https://scheduled-payments.procarepay.com/api.execute.
Token Caching
- Cache tokens: Do not request a new token for every API call. Reuse the cached token until it is close to expiry.
- Proactive refresh: Refresh the token 5–10 minutes before expiry to avoid service interruptions.
- Handle 401 automatically: On a 401 response, refresh the token and retry the request exactly once.
- Secure storage: Store tokens in memory or encrypted storage — never log or persist them in plain text.
Scheduling Best Practices
Always Use UTC
The API stores and evaluates all schedule dates and hours in UTC. Convert merchant local time to UTC
before submitting. A payment intended to run at 5:00 PM Eastern Standard Time (UTC-5) must be submitted
as scheduleHour: 22.
Creating Groups with Payments in One Call
The POST /groups endpoint accepts an optional payments array. Including payments at
group creation time is the most efficient pattern and reduces the number of API calls.
// Preferred: single call to create group + payments
POST /rest/merchants/{MID}/groups
{
"procarePayId": "32350000576",
"scheduleDate": "2030-12-31",
"scheduleHour": 22,
"locationId": 12,
"payments": [
{ "profileId": "...", "amount": 125.00 },
{ "profileId": "...", "amount": 75.00 }
]
}
Group Modifications Before Processing
- Reschedule early: Update the group's
scheduleDateorscheduleHouras soon as a change is needed — the system will pick up the new schedule. - Never modify a processed group: Once a group has been processed, updates to scheduling fields are ignored. Use void-or-refund instead.
- Delete, don't set Deleted: Use the DELETE endpoint to remove a group — do not attempt to set the
deletedflag manually via PUT.
Error Handling and Resilience
HTTP Status Code Reference
| Status Code | Meaning | Action |
|---|---|---|
| 200 / 201 | Success | Process the response data normally. |
| 204 | No Content (delete success) | The resource was deleted. No body is returned. |
| 400 | Bad Request | Fix the request — check required fields, data formats, and validation rules. Do not retry without changes. |
| 401 | Unauthorized | Token is expired or missing. Refresh the token and retry once. |
| 403 | Forbidden | The authenticated client is not authorized for this MID, or the endpoint is not available in this environment. |
| 404 | Not Found | The resource does not exist or does not belong to the specified merchant. Verify IDs. Do not retry. |
| 409 | Conflict | The resource already exists. Do not retry — inspect the existing resource instead. |
| 500 | Internal Server Error | Unexpected server-side error. Retry with exponential backoff (max 3 attempts). Report if persistent. |
Error Response Format
All error responses follow this structure:
{
"message": "Payment group abc123 was not found or does not belong to merchant.",
"responseCode": 404
}
Retry Strategy: Exponential Backoff
For transient errors (500, 503), implement exponential backoff:
- First retry: Wait 1 second
- Second retry: Wait 2 seconds
- Third retry: Wait 4 seconds
- Maximum attempts: 3 (original + 2 retries)
- Add jitter: ±20% random jitter to avoid thundering-herd problems
wait = base_delay * 2^attempt + random_jitter
Where
base_delay = 1s and random_jitter is between −200 ms and +200 ms.
Idempotency
Why It Matters
Scheduled payment groups represent future financial transactions. Accidentally creating duplicate groups can result in customers being charged multiple times. Network timeouts do not mean the request failed — it may have succeeded on the server side.
Idempotent Group Creation
- Check before create: Before creating a group, query
GET /groupsfiltered by date/location to check if an equivalent group already exists. - Store group IDs immediately: As soon as a group is created, persist the returned
paymentGroupIdin your system. - Use custom attributes on payments: Store your own order/invoice reference in
customAttributesso you can detect and look up payments later.
// Idempotent creation pattern 1. Query GET /groups?date=2030-12-31&locationID=12 for MID 2. If matching group already exists → use that group's ID 3. If no match → POST /groups to create new group 4. Store returned paymentGroupId in your database immediately
Email Notifications
sendPaymentScheduledNotification
When set to true at payment creation time, an email is sent to the payor confirming
that their payment has been scheduled. This flag is immutable after creation — you
cannot enable or disable it via PUT. Plan accordingly before creating the payment.
sendEmailReceipt
When set to true, an email receipt is sent to the payor after the payment settles.
Unlike the scheduling notification, this flag can be updated at any time before
the group is processed via the PUT payment endpoint.
sendPaymentScheduledNotification
via PUT will return a 400 error with an ImmutableFieldViolation message.
Always set it correctly at the time you call POST /payments (or POST /groups).
Performance
Pagination
GET endpoints that return collections support optional pagination via pageNumber and
pageSize query parameters. When these are omitted, the full result set is returned as
a plain array. When provided, the response is wrapped in a paginated envelope:
{
"meta": {
"page": 1,
"pageSize": 10,
"totalItems": 42,
"totalPages": 5
},
"items": [ ... ]
}
Use pagination for merchants with large numbers of groups to avoid large response payloads and improve client-side rendering performance.
Filtering
GET /groups and GET /merchants/{MID} both support date, time, and
locationID query parameters. Always filter to the narrowest reasonable scope to reduce
payload size and processing time.
HTTP Client Reuse
- Reuse HTTP client instances: Do not create a new HTTP client per request — reuse a shared instance across all calls.
- Connection pooling: Configure an appropriate connection pool size (typically 10–50 for most workloads).
- Keep-Alive: Enable HTTP keep-alive to reuse TCP connections across consecutive requests.
Security
Credential Management
- Environment variables: Store client ID and client secret in environment variables or a secrets manager (AWS Secrets Manager, Azure Key Vault). Never hard-code them.
- Never commit secrets: Add credential files to
.gitignore. - Rotate regularly: Establish a quarterly or semi-annual rotation schedule for API credentials.
Logging
HTTPS Only
- Use TLS 1.2 or higher for all API communication.
- Always validate TLS certificates — never disable certificate verification in production.
Testing
Non-Production Process Payments Endpoints
The /process-payments endpoints are available only in non-production environments and
only for MIDs in the test range 32350000000–32350099999. Use these endpoints to validate
your end-to-end flow without waiting for the scheduled time to arrive:
- Create a group with payments using a test MID
- Trigger processing for that group immediately
- Verify the payment auth responses on the returned payment objects
- Test void/refund flows using void-or-refund
Test Checklist
| Scenario | Expected Outcome |
|---|---|
| Create group with valid payments | 201 Created, group and payment IDs returned |
| Create group with invalid MID format | 400 Bad Request, descriptive error message |
| Get group with unknown GroupID | 404 Not Found |
| Update payment's sendPaymentScheduledNotification after creation | 400 Bad Request — ImmutableFieldViolation |
| Trigger processing in non-production | 200 OK with triggeredGroups list |
| Trigger processing in production | 403 Forbidden |
| Expired bearer token | 401 Unauthorized — refresh token and retry |
| Void/refund an unprocessed payment | 200 OK — payment skipped, skippedCount incremented |
Production Readiness Checklist
Authentication & Security
- ✓ Cognito client credentials stored securely (not hard-coded)
- ✓ Token caching implemented with proactive refresh
- ✓ Automatic 401 handling with single retry
- ✓ All API calls use HTTPS with TLS 1.2+
- ✓ Logging excludes tokens and sensitive data
Scheduling
- ✓ All schedule times converted to UTC before submission
- ✓ DST transition handling verified
- ✓ Group IDs persisted immediately after creation
- ✓ sendPaymentScheduledNotification set correctly at payment creation
Resilience
- ✓ Exponential backoff retry for 500/503 errors
- ✓ Idempotency check before group creation
- ✓ Timeout configuration (connection: 10s, read: 30s)
- ✓ HTTP client reuse / connection pooling
Observability
- ✓ Correlation IDs logged for all requests
- ✓ Group IDs and payment IDs stored in your database
- ✓ Alerts on error rate spikes and processing failures