List All Users
Overview
Retrieve a paginated list of all Users (API credentials) across your Applications. Use to audit active keys, track environments, and manage access.
Resource Access
- User Permissions: Admin users only
- Endpoint:
GET /users
Arguments
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Number of results per page (default: 20, max: 100) |
| after_cursor | string | No | Cursor for next page |
| before_cursor | string | No | Cursor for previous page |
Example Request
curl -X GET \
'https://api.ahrvo.network/payments/na/users?limit=50' \
-u username:password
Example Response
{
"_embedded": {
"users": [
{
"id": "USuser456",
"created_at": "2023-12-10T20:00:00Z",
"updated_at": "2023-12-10T20:00:00Z",
"enabled": true,
"role": "ROLE_MERCHANT",
"tags": {
"environment": "production",
"purpose": "web_checkout",
"created_by": "admin@example.com"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/users/USuser456"
},
"application": {
"href": "https://api.ahrvo.network/payments/na/applications/APapplication123"
}
}
},
{
"id": "USuser457",
"created_at": "2023-12-09T15:30:00Z",
"updated_at": "2023-12-09T15:30:00Z",
"enabled": false,
"role": "ROLE_MERCHANT",
"tags": {
"environment": "production",
"purpose": "web_checkout",
"rotation_date": "2023-12-10",
"replaced_by": "USuser456"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/users/USuser457"
},
"application": {
"href": "https://api.ahrvo.network/payments/na/applications/APapplication123"
}
}
},
{
"id": "USuser458",
"created_at": "2023-12-08T10:00:00Z",
"updated_at": "2023-12-08T10:00:00Z",
"enabled": true,
"role": "ROLE_MERCHANT",
"tags": {
"environment": "development",
"purpose": "local_testing",
"developer": "john@example.com"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/users/USuser458"
},
"application": {
"href": "https://api.ahrvo.network/payments/na/applications/APapplication123"
}
}
}
]
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/users?limit=50"
},
"next": {
"href": "https://api.ahrvo.network/payments/na/users?limit=50&after_cursor=dXNlcjQ1OA"
}
},
"page": {
"limit": 50,
"offset": 0,
"count": 3
}
}
Additional Information
- No Password: Passwords never returned
passwordfield only in POST response (creation)- Cannot retrieve password later
- Security feature
- If lost, create new User
- Pagination: Cursor-based
limit: Results per page (default: 20, max: 100)after_cursor: Get next pagebefore_cursor: Get previous page- Follow
_links.nextfor next page
- Enabled Status: Filter manually
- No query parameter to filter by
enabled - Filter in your application
true: User can authenticatefalse: User disabled (cannot authenticate)
- No query parameter to filter by
- Role: Permission level
- ROLE_MERCHANT: Standard merchant access
- ROLE_PARTNER: Platform/partner access
- Inherited from Application
- Cannot filter by role in API
- Tags: Custom metadata
- Use to organize keys
- Filter manually by tags
- Track environment, purpose, creator
- No built-in tag filtering
- Application Link: Parent application
_links.applicationshows which Application- Users belong to specific Application
- Multiple Applications = multiple User lists
- Follow link for Application details
- Created At: Creation timestamp
- ISO 8601 format
- UTC timezone
- Use to find old keys
- Sort by age
- Updated At: Last modification
- Changes when tags updated
- Changes when enabled/disabled
- Use to track recent changes
- Sorting: By creation date
- Newest first (default)
- Cannot change sort order
- Paginate for all results
Use Cases
Security Audit
// List all Users and check status
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
// Find enabled Users
const activeUsers = users.filter(u => u.enabled);
console.log(`Active API keys: ${activeUsers.length}`);
// Find old Users (> 90 days)
const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
const oldUsers = users.filter(u => {
return new Date(u.created_at) < ninetyDaysAgo && u.enabled;
});
console.log(`Keys older than 90 days: ${oldUsers.length}`);
oldUsers.forEach(u => {
console.log(`- ${u.id}: Created ${u.created_at}`);
console.log(` Tags: ${JSON.stringify(u.tags)}`);
});
Environment Inventory
# List all Users and group by environment
curl -X GET \
'https://api.ahrvo.network/payments/na/users?limit=100' \
-u username:password | \
jq '.._embedded.users | group_by(.tags.environment)'
# Output:
# Production: 5 keys
# Staging: 3 keys
# Development: 7 keys
Find Disabled Keys
// List all Users and find disabled ones
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
const disabledUsers = users.filter(u => !u.enabled);
console.log('Disabled Users (rotated/revoked):');
disabledUsers.forEach(u => {
console.log(`- ${u.id}`);
console.log(` Disabled: ${u.updated_at}`);
console.log(` Reason: ${u.tags.disabled_reason || 'Not specified'}`);
console.log(` Replaced by: ${u.tags.replaced_by || 'None'}`);
});
Key Rotation Tracking
// Find Users due for rotation
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
const rotationThreshold = 90; // days
const now = new Date();
const dueForRotation = users.filter(u => {
if (!u.enabled) return false;
const created = new Date(u.created_at);
const ageInDays = (now - created) / (1000 * 60 * 60 * 24);
return ageInDays >= rotationThreshold;
});
console.log('Keys due for rotation:');
dueForRotation.forEach(u => {
const created = new Date(u.created_at);
const ageInDays = Math.floor((now - created) / (1000 * 60 * 60 * 24));
console.log(`- ${u.id}: ${ageInDays} days old`);
console.log(` Environment: ${u.tags.environment}`);
console.log(` Purpose: ${u.tags.purpose}`);
});
Compliance Report
// Generate compliance report for all API keys
async function generateKeyAuditReport() {
const allUsers = [];
let cursor = null;
// Paginate through all Users
do {
const url = cursor
? `/users?after_cursor=${cursor}`
: `/users?limit=100`;
const response = await fetch(url);
const data = await response.json();
allUsers.push(...data._embedded.users);
cursor = data._links.next
? extractCursor(data._links.next.href)
: null;
} while (cursor);
// Analyze
const report = {
total: allUsers.length,
enabled: allUsers.filter(u => u.enabled).length,
disabled: allUsers.filter(u => !u.enabled).length,
byEnvironment: {},
byRole: {},
oldKeys: [],
untagged: []
};
allUsers.forEach(user => {
// Count by environment
const env = user.tags?.environment || 'untagged';
report.byEnvironment[env] = (report.byEnvironment[env] || 0) + 1;
// Count by role
report.byRole[user.role] = (report.byRole[user.role] || 0) + 1;
// Find old keys
const ageInDays = (new Date() - new Date(user.created_at)) / (1000 * 60 * 60 * 24);
if (ageInDays > 90 && user.enabled) {
report.oldKeys.push({
id: user.id,
age: Math.floor(ageInDays),
environment: user.tags?.environment
});
}
// Find untagged keys
if (!user.tags || Object.keys(user.tags).length === 0) {
report.untagged.push(user.id);
}
});
return report;
}
Best Practices
- Regular Audits: Review Users periodically
- Monthly or quarterly
- Check for old keys
- Verify all keys are tagged
- Disable unused keys
- Document active keys
- Tag Consistently: Always tag Users
environment: production, staging, developmentpurpose: web_app, admin_panel, reportingcreated_by: Who createdteam: Which team owns- Makes auditing easier
- Track Disabled Keys: Document why disabled
- Add
disabled_reasontag - Add
disabled_datetag - Add
replaced_bytag if rotated - Helps future audits
- Add
- Pagination: Handle large lists
- Use cursor pagination
- Don't assume all Users fit in one page
- Process page by page
- Cache results if needed
- Filter Client-Side: No server filters
- List all Users
- Filter by tags in your code
- Filter by enabled status
- Filter by creation date
- Monitor Creation: Track who creates Users
- Log User creation events
- Alert on unusual activity
- Require approval for production keys
- Audit trail
Common Workflows
Monthly Security Audit
- List all Users
- Filter for enabled Users
- Check age of each User
- Identify Users > 90 days old
- Plan rotation for old keys
- Check for untagged Users
- Verify all production keys are documented
- Generate audit report
Find Keys to Rotate
async function findKeysToRotate() {
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
const productionKeys = users.filter(u =>
u.enabled &&
u.tags?.environment === 'production'
);
const rotationAge = 90; // days
const toRotate = productionKeys.filter(u => {
const age = (new Date() - new Date(u.created_at)) / (1000 * 60 * 60 * 24);
return age >= rotationAge;
});
return toRotate.map(u => ({
id: u.id,
age: Math.floor((new Date() - new Date(u.created_at)) / (1000 * 60 * 60 * 24)),
purpose: u.tags?.purpose,
created_at: u.created_at
}));
}
Cleanup Disabled Keys
// Find disabled keys older than 30 days (safe to delete)
async function findOldDisabledKeys() {
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const oldDisabled = users.filter(u => {
return !u.enabled &&
new Date(u.updated_at) < thirtyDaysAgo;
});
console.log(`Found ${oldDisabled.length} disabled keys older than 30 days`);
// Review before deletion
oldDisabled.forEach(u => {
console.log(`${u.id}: Disabled ${u.updated_at}`);
});
return oldDisabled;
}
Export for Documentation
// Export all active keys for documentation
async function exportActiveKeys() {
const response = await listUsers({ limit: 100 });
const users = response._embedded.users;
const activeKeys = users
.filter(u => u.enabled)
.map(u => ({
id: u.id,
environment: u.tags?.environment,
purpose: u.tags?.purpose,
created: u.created_at,
team: u.tags?.team || u.tags?.created_by
}));
// Export to CSV
const csv = [
'ID,Environment,Purpose,Created,Team',
...activeKeys.map(k =>
`${k.id},${k.environment},${k.purpose},${k.created},${k.team}`
)
].join('\n');
return csv;
}
Security Considerations
- Access Control: Limit who can list Users
- Only admin/security team
- Lists all API credentials
- Sensitive information
- Audit access to this endpoint
- Disabled Keys: Don't delete immediately
- Disable first
- Wait 30+ days
- Verify no longer used
- Then consider deletion
- Allows rollback if needed
- Old Keys: Rotate regularly
- 90 days maximum recommended
- 30-60 days for high security
- Automated rotation ideal
- Track rotation schedule
- Untagged Keys: Investigate
- May be forgotten/orphaned
- Hard to track ownership
- Potential security risk
- Tag or disable
Related Endpoints
- POST /applications/{id}/users: Create new User
- GET /users/{id}: Fetch specific User details
- PUT /users/{id}: Update User (disable, update tags)
- GET /applications/{id}: View parent Application