Summary and recommendation
EasyPost's REST API uses HTTP Basic Auth with the API key as the username and an empty password. There are no OAuth scopes - access level is determined entirely by whether you use a Test or Production key, and by whether the authenticating account is a parent or child.
The User object is the core resource for provisioning; it differs structurally between parent accounts (which include a `children` array) and child accounts (which do not).
No SCIM 2.0 endpoint exists. No SSO or IdP integration is documented. All identity lifecycle operations must be driven directly through the EasyPost REST API.
For teams building an identity graph across a multi-tenant architecture, EasyPost's parent/child account model - accessible via `GET /v2/users/{id}/children` with cursor-based pagination - is the only native mechanism for mapping account relationships at scale.
Rate limits are enforced server-side but not publicly documented. HTTP 429 responses should trigger exponential backoff. The `GET /v2/api_keys` endpoint returns all test and production keys for the parent and every child account simultaneously - treat it as a privileged endpoint and restrict access accordingly.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth (API key as username, empty password) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | N/A |
Authentication
Auth method: HTTP Basic Auth (API key as username, empty password)
Setup steps
- Log in to the EasyPost dashboard and navigate to API Keys.
- Copy your Test or Production API key.
- Pass the API key as the HTTP Basic Auth username with an empty password, or set the Authorization header as 'Authorization: Basic base64(API_KEY:)'.
- Use the Production key for live operations; use the Test key for sandbox/testing.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique identifier prefixed with 'user_' | auto-generated | immutable | |
| object | string | Always 'User' | auto-generated | immutable | |
| parent_id | string | ID of the parent user account; null for top-level accounts | set by system | immutable | |
| name | string | Full name of the user or company name | required | optional | |
| string | Email address for the account | required | optional | Must be unique | |
| phone_number | string | Contact phone number | optional | optional | |
| balance | string | Current account balance (decimal string) | auto | immutable via API | Read-only |
| recharge_amount | string | Amount to recharge when balance falls below threshold | optional | optional | |
| secondary_recharge_amount | string | Secondary recharge amount | optional | optional | |
| recharge_threshold | string | Balance threshold that triggers auto-recharge | optional | optional | |
| children | array | List of child User objects under this account | auto | managed via child user endpoints | Only present on parent accounts |
| api_keys | array | API key objects associated with the user | auto-generated | immutable | Retrieved via /api_keys endpoint |
| created_at | datetime | ISO 8601 timestamp of account creation | auto | immutable | |
| updated_at | datetime | ISO 8601 timestamp of last update | auto | auto |
Core endpoints
Retrieve current user
- Method: GET
- URL:
https://api.easypost.com/v2/users - Watch out for: Returns the authenticated user's own record. Use the Production API key to see live data.
Request example
GET /v2/users
Authorization: Basic {base64(API_KEY:)}
Response example
{
"id": "user_abc123",
"object": "User",
"name": "Acme Corp",
"email": "admin@acme.com",
"balance": "100.00"
}
Update current user
- Method: PUT
- URL:
https://api.easypost.com/v2/users/{id} - Watch out for: The user ID in the path must match the authenticated user's ID; you cannot update other top-level accounts.
Request example
PUT /v2/users/user_abc123
{
"user": {
"name": "Acme Corp Updated",
"phone_number": "555-1234"
}
}
Response example
{
"id": "user_abc123",
"name": "Acme Corp Updated",
"phone_number": "555-1234"
}
Create child user
- Method: POST
- URL:
https://api.easypost.com/v2/users - Watch out for: Child users are sub-accounts under the authenticated parent. They share the parent's carrier accounts unless configured separately.
Request example
POST /v2/users
{
"user": {
"name": "Child Account",
"email": "child@acme.com"
}
}
Response example
{
"id": "user_child456",
"object": "User",
"parent_id": "user_abc123",
"name": "Child Account",
"email": "child@acme.com"
}
Retrieve child user
- Method: GET
- URL:
https://api.easypost.com/v2/users/{id} - Watch out for: Must authenticate as the parent account to retrieve child user details.
Request example
GET /v2/users/user_child456
Authorization: Basic {base64(PARENT_API_KEY:)}
Response example
{
"id": "user_child456",
"parent_id": "user_abc123",
"name": "Child Account"
}
Delete child user
- Method: DELETE
- URL:
https://api.easypost.com/v2/users/{id} - Watch out for: Only child users can be deleted via API. Top-level accounts cannot be deleted through the API.
Request example
DELETE /v2/users/user_child456
Authorization: Basic {base64(PARENT_API_KEY:)}
Response example
HTTP 204 No Content
Retrieve API keys
- Method: GET
- URL:
https://api.easypost.com/v2/api_keys - Watch out for: Returns both test and production keys for the authenticated user and all child accounts. Guard this endpoint carefully.
Request example
GET /v2/api_keys
Authorization: Basic {base64(API_KEY:)}
Response example
{
"id": "user_abc123",
"keys": [
{"object": "ApiKey", "key": "EZAK...", "mode": "production"}
],
"children": []
}
Update child user
- Method: PUT
- URL:
https://api.easypost.com/v2/users/{id} - Watch out for: Authenticate as the parent account when updating child user fields.
Request example
PUT /v2/users/user_child456
{
"user": {
"recharge_amount": "50.00",
"recharge_threshold": "10.00"
}
}
Response example
{
"id": "user_child456",
"recharge_amount": "50.00",
"recharge_threshold": "10.00"
}
List child users
- Method: GET
- URL:
https://api.easypost.com/v2/users/{id}/children - Watch out for: Pagination uses cursor-based before_id parameter. has_more indicates additional pages.
Request example
GET /v2/users/user_abc123/children?page_size=20
Authorization: Basic {base64(API_KEY:)}
Response example
{
"children": [
{"id": "user_child456", "name": "Child Account"}
],
"has_more": false
}
Rate limits, pagination, and events
- Rate limits: EasyPost does not publish explicit per-plan rate limits in its public documentation. Rate limiting exists and is enforced server-side; exceeding limits returns HTTP 429.
- Rate-limit headers: No
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 is returned when rate limits are exceeded. Retry-After header may be present. Contact EasyPost support for specific limits on high-volume accounts.
- Pagination method: cursor
- Default page size: 20
- Max page size: 100
- Pagination pointer: page_size / before_id
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | Not publicly documented | 0 |
- Webhooks available: Yes
- Webhook notes: EasyPost supports webhooks for shipment and tracker events. There are no dedicated user-management webhook events (e.g., user created/deleted).
- Alternative event strategy: Poll GET /v2/users/{id}/children for child account changes.
- Webhook events: tracker.created, tracker.updated, payment.created, refund.successful, batch.created, batch.updated, scan_form.created, insurance.purchased, insurance.cancelled, insurance.failed
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: N/A
- Endpoint: Not documented
Limitations:
- EasyPost does not offer a SCIM 2.0 API.
- No SSO/IdP integration (Okta, Entra, Google Workspace) is documented.
- User provisioning must be done via the EasyPost REST API directly.
Common scenarios
Three automation scenarios are well-supported by the API. First, tenant provisioning: authenticate as the parent, POST to /v2/users with name and email, store the returned id and auto-generated API keys, then optionally set recharge_amount and recharge_threshold via PUT. Child accounts share the parent's billing by default, so recharge configuration is a required step in any production provisioning flow.
Second, API key auditing: a single GET /v2/api_keys call returns keys for the parent and all children. Iterate the children array to map each child id to its test and production keys. This endpoint's broad scope makes it a high-value target - restrict it at the application layer in multi-tenant deployments.
Third, deprovisioning: confirm the child user ID via GET, then DELETE /v2/users/{child_id}. A 204 response confirms permanent removal. This operation is irreversible and removes all associated shipment history and API keys for the child account - there is no soft-delete or recovery path via API.
Provision a child account for a new tenant
- Authenticate with the parent account's Production API key.
- POST /v2/users with name and email to create the child user.
- Store the returned child user id and auto-generated API keys.
- Optionally PUT /v2/users/{child_id} to set recharge_amount and recharge_threshold.
- Distribute the child's API key to the tenant for their own API calls.
Watch out for: Child accounts share the parent's billing by default. Ensure recharge thresholds are set to avoid unexpected charges to the parent balance.
Retrieve and audit all child API keys
- Authenticate with the parent account's Production API key.
- GET /v2/api_keys to retrieve keys for the parent and all children.
- Iterate the children array in the response to map each child ID to their test/production keys.
- Log or store keys securely for audit purposes.
Watch out for: This single endpoint exposes all child keys simultaneously. Limit who can call this endpoint in your application.
Deprovision a child account
- Authenticate with the parent account's Production API key.
- Confirm the child user ID via GET /v2/users/{child_id}.
- DELETE /v2/users/{child_id} to permanently remove the child account.
- Verify HTTP 204 response confirming deletion.
Watch out for: Deletion is permanent and cannot be undone via API. All shipment history and API keys for the child are removed.
Why building this yourself is a trap
The primary integration trap is assuming that removing a dashboard team member completes the offboarding process. It does not. API keys issued to or used by that member remain active until explicitly rotated or deleted via the API Keys section.
There is no automated key revocation tied to team member removal, and no audit log to surface which keys a removed member had access to.
A second trap is pagination: EasyPost uses cursor-based pagination (before_id), not offset. Any integration that assumes offset pagination will silently miss records after the first page. Store the last returned id and pass it as before_id on subsequent requests; check has_more to determine whether additional pages exist.
For teams building on top of EasyPost with an MCP server with 60+ deep IT/identity integrations, the absence of SCIM and webhook coverage for user lifecycle events (no user.created or user.deleted events exist) means child account state must be tracked by polling GET /v2/users/{id}/children rather than reacting to push notifications. Design your sync layer accordingly.
Automate EasyPost workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.