Procare Pay Scheduled Payments

Complete API Documentation for Scheduled Payment Processing

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
Note: Production scope does not include environment and is simply 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.
Performance tip: Cognito tokens issued for this API are valid for 6 hours. Caching a single token eliminates hundreds of unnecessary authentication round-trips per day.

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.

Daylight Saving Time: Remember that UTC offsets change when DST transitions occur. Re-evaluate the UTC offset at the time the payment is being scheduled, not the time it was originally planned.

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 scheduleDate or scheduleHour as 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 deleted flag 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
Formula: 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 /groups filtered 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 paymentGroupId in your system.
  • Use custom attributes on payments: Store your own order/invoice reference in customAttributes so 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.

Immutability: Attempting to change 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

Never log bearer tokens or client credentials. Log only correlation IDs, merchant IDs, group IDs, and payment IDs (never payment amounts, profile IDs, or auth responses that contain card data).

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:

  1. Create a group with payments using a test MID
  2. Trigger processing for that group immediately
  3. Verify the payment auth responses on the returned payment objects
  4. 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