{"info":{"_postman_id":"d06596f9-c1ca-4198-a5cb-cd5a9e1ea1b6","name":"Velozity Multi-Tenant API","description":"<html><head></head><body><p><strong>Multi-Tenant REST API for a B2B SaaS platform.</strong></p>\n<p><strong>This system demonstrates:<br>- Tenant isolation using API key-based authentication<br>- Three-tier rate limiting (global, endpoint, burst) using Redis<br>- Queue-based email engine with retry and logging<br>- Tamper-evident audit trail using chained hashing<br>- Health monitoring and usage metrics</strong></p>\n<p><strong>Authentication:<br>- Tenant APIs → X-API-Key<br>- Internal APIs → X-Internal-Key</strong></p>\n<p><strong>Use the seeded API keys from logs to test the system.</strong></p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"27348979","collectionId":"d06596f9-c1ca-4198-a5cb-cd5a9e1ea1b6","publishedId":"2sBXiknB5B","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-03-25T16:39:11.000Z"},"item":[{"name":"Tenants","item":[{"name":"Get My Tenant","id":"63806815-220e-4df2-b916-138098af2548","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/tenants/me","description":"<p>Returns the current tenant details based on the provided API key.</p>\n<p>Used to verify tenant resolution and authentication.</p>\n","urlObject":{"path":["tenants","me"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"63806815-220e-4df2-b916-138098af2548"},{"name":"Create Tenant","id":"ecdde7dd-2328-4f36-a580-76487ce886af","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\r\n  \"name\": \"Test Company2\",\r\n  \"ownerEmail\": \"owner@testcompany2.com\",\r\n  \"ownerName\": \"Test Owner2\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/tenants","description":"<p>Creates a new tenant along with an Owner user.</p>\n<p>This endpoint generates a new API key for the tenant.<br />The raw API key is returned only once and must be saved securely.</p>\n","urlObject":{"path":["tenants"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"ecdde7dd-2328-4f36-a580-76487ce886af"}],"id":"7cc5724b-f1e4-4f9e-b04d-30eb91ec40a0","description":"<p><strong>Endpoints related to tenant management.<br />Each tenant represents an organisation with isolated users, API keys, and data.<br />Tenant context is resolved using the API key, not user input</strong></p>\n","_postman_id":"7cc5724b-f1e4-4f9e-b04d-30eb91ec40a0"},{"name":"Users","item":[{"name":"Update User","id":"06d16489-78e8-4e42-b08e-f17204497108","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"PATCH","header":[{"key":"X-API-Key","value":"","type":"text"}],"body":{"mode":"raw","raw":"{\r\n  \"name\": \"Updated Name\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/users/3149ee6e-e6ed-4a76-85c4-da74395ba386","urlObject":{"path":["users","3149ee6e-e6ed-4a76-85c4-da74395ba386"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"06d16489-78e8-4e42-b08e-f17204497108"},{"name":"List Users","id":"4c46390d-e0e3-4116-9e44-65334550063a","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/users","description":"<p>Returns all users belonging to the current tenant.</p>\n<p>Supports tenant isolation — only users from the authenticated tenant are visible.</p>\n","urlObject":{"path":["users"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"4c46390d-e0e3-4116-9e44-65334550063a"},{"name":"Delete User","id":"1049d42b-16f0-429b-bacf-246b8d424020","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"DELETE","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/users/feadea96-0bd2-4b91-b88d-af4ce654a895","description":"<p>Deletes a user from the tenant.</p>\n<p>Operation is logged in the audit trail.</p>\n","urlObject":{"path":["users","feadea96-0bd2-4b91-b88d-af4ce654a895"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"1049d42b-16f0-429b-bacf-246b8d424020"},{"name":"Get User by ID","id":"0229c82b-3df4-4532-b000-6ce223b4265c","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/users/3149ee6e-e6ed-4a76-85c4-da74395ba386","urlObject":{"path":["users","3149ee6e-e6ed-4a76-85c4-da74395ba386"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"0229c82b-3df4-4532-b000-6ce223b4265c"},{"name":"Create User (Invite)","id":"6a183b77-2d78-4285-a8b9-9d41b9344ae7","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"X-API-Key","value":"","type":"text"}],"body":{"mode":"raw","raw":"{\r\n  \"email\": \"newmember@acme.com\",\r\n  \"name\": \"New Member\",\r\n  \"role\": \"MEMBER\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/users","urlObject":{"path":["users"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"6a183b77-2d78-4285-a8b9-9d41b9344ae7"}],"id":"83992eee-158a-44e6-9742-80bc61452dee","description":"<p><strong>User management within a tenant.</strong></p>\n<p><strong>All operations are strictly scoped to the authenticated tenant.<br />Cross-tenant access is not possible.</strong></p>\n","_postman_id":"83992eee-158a-44e6-9742-80bc61452dee"},{"name":"API Keys","item":[{"name":"List API Keys","id":"a2715ac2-e64e-492b-bd01-6103939e1fdb","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/api-keys","description":"<p>Lists all active API keys for the tenant.</p>\n","urlObject":{"path":["api-keys"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"a2715ac2-e64e-492b-bd01-6103939e1fdb"},{"name":"Rotate API Key","id":"d5158af5-61ea-4066-970d-dd82a29d3517","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/api-keys/rotate","description":"<p>Rotates the current API key.</p>\n<p>The old key remains valid for 15 minutes (grace period), then expires.</p>\n","urlObject":{"path":["api-keys","rotate"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"d5158af5-61ea-4066-970d-dd82a29d3517"}],"id":"6e50a18d-784c-4118-9a64-915d8a480027","description":"<p><strong>API key management for tenant authentication.</strong></p>\n<p><strong>API keys are hashed before storage and shown only once at creation.</strong></p>\n","_postman_id":"6e50a18d-784c-4118-9a64-915d8a480027"},{"name":"Audit","item":[{"name":"Get Audit Logs","id":"d0996cb9-44be-4f02-bdad-cc90ff0649b3","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/audit?limit=5","description":"<p>Fetches audit logs with cursor-based pagination.</p>\n<p>Supports filtering and ensures efficient large dataset handling.</p>\n","urlObject":{"path":["audit"],"host":[""],"query":[{"key":"limit","value":"5"}],"variable":[]}},"response":[],"_postman_id":"d0996cb9-44be-4f02-bdad-cc90ff0649b3"},{"name":"Get Audit Logs - Page 2","id":"7c9f4640-b5ac-4100-b89c-97b8986d1aeb","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/audit?limit=5&cursor=eyJpZCI6IjIzYTdkMzk3LWI0OWUtNDhhOS04NDNlLTc2MmNjYTAxMzk5MyJ9","description":"<p>Fetches the next page of audit logs using a cursor.</p>\n<p>Demonstrates cursor-based pagination instead of offset.</p>\n","urlObject":{"path":["audit"],"host":[""],"query":[{"key":"limit","value":"5"},{"key":"cursor","value":"eyJpZCI6IjIzYTdkMzk3LWI0OWUtNDhhOS04NDNlLTc2MmNjYTAxMzk5MyJ9"}],"variable":[]}},"response":[],"_postman_id":"7c9f4640-b5ac-4100-b89c-97b8986d1aeb"},{"name":"Verify Audit Chain","id":"4ee26a67-b275-4ded-81b8-4216ea8ea4ed","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/audit/verify","description":"<p>Verifies the integrity of the audit log chain.</p>\n<p>Recomputes hashes and detects any tampering.</p>\n<p>Returns:</p>\n<ul>\n<li><p>valid: true/false</p>\n</li>\n<li><p>brokenEntryId (if tampered)</p>\n</li>\n</ul>\n","urlObject":{"path":["audit","verify"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"4ee26a67-b275-4ded-81b8-4216ea8ea4ed"}],"id":"8625ad1c-968d-468a-bb30-fa1d3902a196","description":"<p><strong>Verifies the integrity of the audit log chain using SHA-256 hashing.<br />Ensures no tampering has occurred across all entries.<br />Returns whether the chain is intact and total entries verified.</strong></p>\n","_postman_id":"8625ad1c-968d-468a-bb30-fa1d3902a196"},{"name":"System","item":[{"name":"Health Check","id":"0e05ce58-cf39-409a-992f-ca64327cfd91","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-Internal-Key","value":"","type":"text"}],"url":"/health","description":"<p><strong>Provides overall system health including API, database, Redis, and queue status.<br />Includes latency and uptime metrics.<br />Used for monitoring system reliability.</strong></p>\n","urlObject":{"path":["health"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"0e05ce58-cf39-409a-992f-ca64327cfd91"},{"name":"Metrics","id":"fe2674c5-5b7c-4019-b27c-807231117da4","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-Internal-Key","value":"","type":"text"}],"url":"/metrics?tenantId=9cb50120-81a8-4322-b3d7-37cbaab1ef50","description":"<p><strong>Returns tenant-specific usage statistics for the current billing period.<br />Includes request counts, rate limit breaches, and email delivery stats.<br />Useful for analytics and monitoring.</strong></p>\n","urlObject":{"path":["metrics"],"host":[""],"query":[{"disabled":true,"key":"tenantId","value":"335c3f90-8929-4518-a532-ec99859d71b4"},{"key":"tenantId","value":"9cb50120-81a8-4322-b3d7-37cbaab1ef50"}],"variable":[]}},"response":[],"_postman_id":"fe2674c5-5b7c-4019-b27c-807231117da4"}],"id":"9521ee9d-2b34-444c-acce-a342a6ea34ca","description":"<p><strong>Internal system monitoring and observability endpoints.</strong></p>\n<p><strong>Protected using X-Internal-Key.</strong></p>\n","_postman_id":"9521ee9d-2b34-444c-acce-a342a6ea34ca"},{"name":"Tests","item":[{"name":"Unauthorized Test","id":"f5e805e1-687d-419f-8bab-61a306254dc9","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"/users","description":"<p><strong>Attempts to access protected endpoint without API key.</strong></p>\n<p><strong>Expected: 401 Unauthorized</strong></p>\n","urlObject":{"path":["users"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"f5e805e1-687d-419f-8bab-61a306254dc9"},{"name":"Isolation Test - Access Tenant 2 Data with Tenant 1 Key","id":"da06333b-f2db-4a85-8c25-73dc65adc607","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/users","description":"<p><strong>Attempts to access another tenant’s data using a different API key.</strong></p>\n<p><strong>Expected: No cross-tenant data leakage</strong></p>\n","urlObject":{"path":["users"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"da06333b-f2db-4a85-8c25-73dc65adc607"},{"name":"Rate Limit Test","id":"df4117f5-db59-4435-bfb4-828132c212be","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"X-API-Key","value":"","type":"text"}],"url":"/users","description":"<p><strong>Indicates API usage limits for each request.<br />Shows allowed requests, remaining count, and reset time.<br />Helps clients avoid exceeding rate limits.</strong></p>\n","urlObject":{"path":["users"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"df4117f5-db59-4435-bfb4-828132c212be"}],"id":"2f03b486-5bb7-4c2e-8472-627e7d6f7cd4","description":"<p><strong>Test scenarios to validate system behavior and constraints.</strong></p>\n","_postman_id":"2f03b486-5bb7-4c2e-8472-627e7d6f7cd4"}],"event":[{"listen":"prerequest","script":{"id":"49f313a3-82d2-46ab-a7e8-d8d1e3167287","type":"text/javascript","packages":{},"requests":{},"exec":[""]}},{"listen":"test","script":{"id":"68f4dc53-4285-431c-a095-f6a6469e9cae","type":"text/javascript","packages":{},"requests":{},"exec":[""]}}],"variable":[{"key":"baseUrl","value":""},{"key":"tenant1ApiKey","value":""},{"key":"tenant2ApiKey","value":""},{"key":"internalApiKey","value":""},{"key":"tenant1Id","value":""},{"key":"tenant2Id","value":""}]}