Team members represent the people who have access to your EPD merchant account.

Each team member belongs to your merchant and is assigned a role that controls what they can see and do in the dashboard. Use these endpoints to invite new team members and review who currently has access.

Invitation flow

When you create a team member, EPD sends them an email so they can finish setting up their access. The team member starts in pending status and moves to active after they accept:

  • If the email already belongs to an EPD account, the message invites them to join your merchant account.
  • Otherwise, the message walks them through creating a password and signing in for the first time.

Permissions

Endpoint Required permission
GET /v1/roles team_members:read
POST /v1/team_members team_members:write
GET /v1/team_members team_members:read
GET /roles

List roles

Returns the roles available within your merchant account, sorted alphabetically by name.

Use this endpoint to discover the per-merchant role_id you pass to POST /v1/team_members.

Expanding permissions

By default each role includes id, name, description, and default_page. Pass ?expand=permissions to also include the underlying permission keys for each role.

Query parameters

NameTypeDescription
expand
string
Comma-separated list of relations to expand. Supported: `permissions`.
e.g. "permissions"

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"

Code samples

# List roles
curl https://api.epd.com/v1/roles \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "EPD-Version: 2026-02-11"

# Include permissions
curl "https://api.epd.com/v1/roles?expand=permissions" \
  -H "Authorization: Bearer epd_test_sk_xxxx"
const response = await fetch(
  'https://api.epd.com/v1/roles?expand=permissions',
  {
    headers: {
      'Authorization': 'Bearer epd_test_sk_xxxx',
      'EPD-Version': '2026-02-11',
    },
  }
);

const { data } = await response.json();
for (const role of data) {
  console.log(role.id, role.name, role.permissions?.length ?? 0);
}
import requests

response = requests.get(
    'https://api.epd.com/v1/roles',
    headers={
        'Authorization': 'Bearer epd_test_sk_xxxx',
        'EPD-Version': '2026-02-11',
    },
    params={'expand': 'permissions'},
)

for role in response.json()['data']:
    print(role['id'], role['name'])

Responses

200 Roles available within the authenticated merchant. Owner is excluded.
FieldTypeDescription
datarequired
array[Role]
urlrequired
string
e.g. "/v1/roles"
401 Unauthorized — Authentication failed.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
403 Forbidden — API key missing `team_members:read`.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
429 Too Many Requests — Rate limit exceeded.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
POST /team_members

Create a team member

Adds a new team member to your merchant account and emails them an invitation so they can finish setting up access.

The new member starts in pending status and moves to active after they accept the invitation.

Restrictions

  • Each email can only be a member of your merchant once. Re-inviting an email that already has an active or pending membership returns 409 resource_already_exists.
  • phone_number must be a US number in E.164 format+1 followed by exactly 10 digits (e.g., +15551234567).

Discovering role_id

Call GET /v1/roles to look up the role_id you want to assign. You can cache these IDs — they don't change unless a role is renamed or deleted from the dashboard.

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"
X-EPD-Idempotency-Keyrequired
string (uuid)
UUID v4 idempotency key. Same key with same request returns cached response. Keys expire after 24 hours.
e.g. "550e8400-e29b-41d4-a716-446655440000"

Request body required

FieldTypeDescription
first_namerequired
string
Team member's first name.
e.g. "Jane"
last_namerequired
string
Team member's last name.
e.g. "Doe"
emailrequired
string (email)
Team member's email address. Used as their login identifier.
e.g. "jane@example.com"
phone_numberrequired
string
US phone number in E.164 format — `+1` followed by exactly 10 digits.
e.g. "+15551234567"
role_idrequired
string (uuid)
UUID of a merchant role to assign. Use `GET /v1/roles` to list available role IDs. The Owner role cannot be assigned and will return `403 insufficient_permissions`.
e.g. "c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f"

Code samples

curl -X POST https://api.epd.com/v1/team_members \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "Content-Type: application/json" \
  -H "EPD-Version: 2026-02-11" \
  -H "X-EPD-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "role_id": "c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f"
  }'
const response = await fetch('https://api.epd.com/v1/team_members', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer epd_test_sk_xxxx',
    'Content-Type': 'application/json',
    'EPD-Version': '2026-02-11',
    'X-EPD-Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    first_name: 'Jane',
    last_name: 'Doe',
    email: 'jane@example.com',
    phone_number: '+15551234567',
    role_id: 'c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f',
  }),
});

const teamMember = await response.json();
console.log(teamMember.id, teamMember.status); // ..., 'pending'
import uuid
import requests

response = requests.post(
    'https://api.epd.com/v1/team_members',
    headers={
        'Authorization': 'Bearer epd_test_sk_xxxx',
        'EPD-Version': '2026-02-11',
        'X-EPD-Idempotency-Key': str(uuid.uuid4()),
    },
    json={
        'first_name': 'Jane',
        'last_name': 'Doe',
        'email': 'jane@example.com',
        'phone_number': '+15551234567',
        'role_id': 'c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f',
    }
)

team_member = response.json()
print(team_member['id'], team_member['status'])

Responses

201 Team member created. An invitation or set-password email is sent.
FieldTypeDescription
idrequired
string (uuid)
Unique identifier for this team member (UUID).
e.g. "8f9e1c5a-3b2d-4f6e-9a1b-2c3d4e5f6a7b"
emailrequired
string (email)
Team member's email address.
e.g. "jane@example.com"
first_namerequired
string
Team member's first name.
e.g. "Jane"
last_namerequired
string
Team member's last name.
e.g. "Doe"
phone_numbernullable
string
US phone number in E.164 format (`+1` followed by 10 digits).
e.g. "+15551234567"
statusrequired
enum
Membership status: - `pending` — invitation sent, awaiting acceptance / password setup - `active` — member has accepted and can sign in - `blocked` — access has been revoked
pendingactiveblocked
e.g. "pending"
rolerequired
object
The merchant role assigned to this team member.
idrequired
string (uuid)
e.g. "c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f"
namerequired
string
e.g. "Manager"
created_atrequired
string (date-time)
ISO 8601 timestamp of creation.
e.g. "2026-05-08T10:30:00.000Z"
updated_atrequired
string (date-time)
ISO 8601 timestamp of last update.
e.g. "2026-05-08T10:30:00.000Z"
400 Bad Request — The request was invalid or cannot be served.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
401 Unauthorized — Authentication failed.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
403 Forbidden — API key missing `team_members:write`, or attempted to assign the Owner role.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
404 Not Found — the supplied `role_id` doesn't exist in your merchant.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
409 Conflict — The request conflicts with existing data.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
429 Too Many Requests — Rate limit exceeded.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
GET /team_members

List team members

Returns a paginated list of team members for the authenticated merchant, sorted by creation date (newest first by default).

Pagination

Uses cursor-based pagination. Use starting_after with the last team member's ID to fetch the next page.

GET /v1/team_members?limit=20
GET /v1/team_members?limit=20&starting_after=8f9e1c5a-3b2d-4f6e-9a1b-2c3d4e5f6a7b

Filtering

Filter by membership status with ?status=pending|active|blocked.

Query parameters

NameTypeDescription
limit
integer
Number of items to return per page.
Default: 10
starting_after
string
Cursor for forward pagination. Returns items created after this ID.
e.g. "550e8400-e29b-41d4-a716-446655440000"
ending_before
string
Cursor for backward pagination. Returns items created before this ID.
e.g. "550e8400-e29b-41d4-a716-446655440001"
status
enum
Filter by membership status.
pendingactiveblocked
e.g. "active"

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"

Code samples

# List first 20 team members
curl https://api.epd.com/v1/team_members?limit=20 \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "EPD-Version: 2026-02-11"

# Only pending invitations
curl "https://api.epd.com/v1/team_members?status=pending" \
  -H "Authorization: Bearer epd_test_sk_xxxx"
const params = new URLSearchParams({ limit: '20', status: 'active' });

const response = await fetch(
  `https://api.epd.com/v1/team_members?${params}`,
  {
    headers: {
      'Authorization': 'Bearer epd_test_sk_xxxx',
      'EPD-Version': '2026-02-11',
    },
  }
);

const { data, has_more, cursors } = await response.json();
import requests

response = requests.get(
    'https://api.epd.com/v1/team_members',
    headers={
        'Authorization': 'Bearer epd_test_sk_xxxx',
        'EPD-Version': '2026-02-11',
    },
    params={'limit': 20, 'status': 'active'},
)

result = response.json()
for member in result['data']:
    print(member['email'], member['role']['name'])

Responses

200 A paginated list of team members.
FieldTypeDescription
data
array[TeamMember]
url
any
e.g. "/v1/team_members"
401 Unauthorized — Authentication failed.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
403 Forbidden — API key missing `team_members:read`.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
429 Too Many Requests — Rate limit exceeded.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.