Verify Identity Keys Test Cases
- Verify Identity Keys API Test Cases
- Overview
- 1. Successful Authorization (200)
- 2. Unauthorized — Invalid or Unknown Key (401)
- 3. Bad Request — Validation Errors (400)
- 3.1 Missing requestId
- 3.2 Invalid requestId Format
- 3.3 Missing identityKey
- 3.4 identityKey Exceeds 1000 Characters
- 3.5 identityKey Not Valid Base64
- 3.6 Missing storeId
- 3.7 storeId Exceeds 255 Characters
- 3.8 Invalid storeId
- 3.9 Missing authEvent
- 3.10 Missing authEvent.id
- 3.11 Invalid authEvent.id Format
- 3.12 Missing authEvent.timestamp
- 3.13 Invalid authEvent.timestamp Format
- 3.14 Missing authEvent.location
- 3.15 Invalid authEvent.location Value
- 3.16 Invalid keyChannel Value
- 3.17 Empty Request Body
- 4. Rate Limiting (429)
- 5. Internal Server Error (500)
- 6. Idempotency
- 7. QR Code / Scan Key Code Validation
- 8. Gate Behavior Validation
- 9. Performance
- Test Data Requirements
- Test Execution Notes
Disclaimer: This document contains sample content for illustrative purposes only. Organizations should follow their own established best practices, security requirements, and compliance standards to ensure solutions are production-ready.
Verify Identity Keys API Test Cases
Overview
These test cases validate the VerifyIdentityKeys API (POST /v1/identity/identity-keys), which Amazon calls whenever a shopper scans their Scan Key Code at the JWO gate. The API is the retailer's Identity Connector — it receives the shopper's identity key and returns an authorization decision that determines whether the gate opens or remains closed.
1. Successful Authorization (200)
1.1 Shopper Entry — Valid Identity Key
Objective: Verify a valid shopper is authorized and the gate opens
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfc2hvcHBlcl9rZXk=",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T17:53:57Z",
"location": "ENTRY"
},
"keyChannel": "OPTICAL"
}
Expected Response (200):
{
"visitorDetails": {
"id": "shopper-uuid-001",
"type": "SHOPPER"
}
}
Expected Result: Gate opens. Shopper is allowed to enter the store.
1.2 Associate Entry
Objective: Verify a store associate is authorized with type ASSOCIATE
Request:
{
"requestId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"identityKey": "YXNzb2NpYXRlX2tleQ==",
"storeId": "STORE-001",
"authEvent": {
"id": "22222222-3333-4444-5555-666666666666",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
},
"keyChannel": "OPTICAL"
}
Expected Response (200):
{
"visitorDetails": {
"id": "associate-uuid-001",
"type": "ASSOCIATE"
}
}
Expected Result: Gate opens. Associate entry is tracked separately from shopper entry.
1.3 Cash Shopper Entry
Objective: Verify a cash shopper is authorized with type CASH_SHOPPER (store must support this type)
Request:
{
"requestId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"identityKey": "Y2FzaF9zaG9wcGVyX2tleQ==",
"storeId": "CASH-ENABLED-STORE-001",
"authEvent": {
"id": "33333333-4444-5555-6666-777777777777",
"timestamp": "2024-02-09T18:05:00Z",
"location": "ENTRY"
},
"keyChannel": "OPTICAL"
}
Expected Response (200):
{
"visitorDetails": {
"id": "cash-shopper-uuid-001",
"type": "CASH_SHOPPER"
}
}
Expected Result: Gate opens. Confirm CASH_SHOPPER type is only returned for stores that support it.
1.4 Entry with Optional shopperDeviceId
Objective: Verify response includes shopperDeviceId when the shopper's mobile device is identified
Request: Same as test 1.1
Expected Response (200):
{
"visitorDetails": {
"id": "shopper-uuid-001",
"type": "SHOPPER"
},
"shopperDeviceId": "device-abc-123"
}
Expected Result: Gate opens. shopperDeviceId is present and correctly identifies the shopper's device.
1.5 Exit Gate Scan
Objective: Verify identity key validation at the EXIT gate location
Request:
{
"requestId": "d4e5f6a7-b8c9-0123-defa-234567890123",
"identityKey": "dmFsaWRfc2hvcHBlcl9rZXk=",
"storeId": "STORE-001",
"authEvent": {
"id": "44444444-5555-6666-7777-888888888888",
"timestamp": "2024-02-09T18:30:00Z",
"location": "EXIT"
},
"keyChannel": "OPTICAL"
}
Expected Response (200):
{
"visitorDetails": {
"id": "shopper-uuid-001",
"type": "SHOPPER"
}
}
Expected Result: Exit gate processes the scan correctly.
1.6 RADIO Key Channel
Objective: Verify identity key validation when keyChannel is RADIO (e.g., badge scan)
Request:
{
"requestId": "e5f6a7b8-c9d0-1234-efab-345678901234",
"identityKey": "cmFkaW9fYmFkZ2Vfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "55555555-6666-7777-8888-999999999999",
"timestamp": "2024-02-09T18:10:00Z",
"location": "ENTRY"
},
"keyChannel": "RADIO"
}
Expected Response (200):
{
"visitorDetails": {
"id": "badge-shopper-uuid-001",
"type": "SHOPPER"
}
}
Expected Result: Gate opens. RADIO channel scan is processed correctly.
1.7 keyChannel Omitted
Objective: Verify the API handles requests without the optional keyChannel field
Request:
{
"requestId": "f6a7b8c9-d0e1-2345-fabc-456789012345",
"identityKey": "dmFsaWRfc2hvcHBlcl9rZXk=",
"storeId": "STORE-001",
"authEvent": {
"id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
"timestamp": "2024-02-09T18:15:00Z",
"location": "ENTRY"
}
}
Expected Response (200):
{
"visitorDetails": {
"id": "shopper-uuid-001",
"type": "SHOPPER"
}
}
Expected Result: Gate opens. Missing optional keyChannel does not cause a failure.
2. Unauthorized — Invalid or Unknown Key (401)
2.1 Invalid Identity Key
Objective: Verify gate remains closed when the identity key is invalid
Request:
{
"requestId": "a1111111-1111-1111-1111-111111111111",
"identityKey": "aW52YWxpZF9rZXk=",
"storeId": "STORE-001",
"authEvent": {
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"timestamp": "2024-02-09T18:20:00Z",
"location": "ENTRY"
},
"keyChannel": "OPTICAL"
}
Expected Response (401):
{
"message": "Invalid identity key"
}
Expected Result: Gate remains closed.
2.2 Identity Key Not Found
Objective: Verify gate remains closed when the identity key does not map to any known shopper
Request:
{
"requestId": "b2222222-2222-2222-2222-222222222222",
"identityKey": "dW5rbm93bl9rZXk=",
"storeId": "STORE-001",
"authEvent": {
"id": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
"timestamp": "2024-02-09T18:25:00Z",
"location": "ENTRY"
},
"keyChannel": "OPTICAL"
}
Expected Response (401):
{
"message": "Identity key not found"
}
Expected Result: Gate remains closed.
2.3 Expired Identity Key
Objective: Verify gate remains closed when the identity key (e.g., QR code) has expired
Test Steps:
- Generate a Scan Key Code
- Wait beyond the configured expiry window (e.g., 30–90 seconds)
- Submit the expired key to the API
Expected Response (401):
{
"message": "Identity key expired"
}
Expected Result: Gate remains closed. Shopper must generate a new Scan Key Code.
2.4 Flagged Shopper Account
Objective: Verify gate remains closed when the shopper's account has been flagged (e.g., fraud, outstanding balance)
Request: Use an identity key mapped to a flagged account
Expected Response (401):
{
"message": "Shopper account flagged"
}
Expected Result: Gate remains closed.
2.5 Expired Payment Method
Objective: Verify gate remains closed when the shopper's associated payment method has expired
Request: Use an identity key mapped to a shopper with an expired card
Expected Response (401):
{
"message": "Payment method expired"
}
Expected Result: Gate remains closed.
3. Bad Request — Validation Errors (400)
3.1 Missing requestId
Objective: Verify 400 when requestId is omitted
Request:
{
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Missing required field: requestId"
}
3.2 Invalid requestId Format
Objective: Verify 400 when requestId does not match UUID format
Request:
{
"requestId": "not-a-valid-uuid",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Invalid requestId format"
}
3.3 Missing identityKey
Objective: Verify 400 when identityKey is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Missing required field: identityKey"
}
3.4 identityKey Exceeds 1000 Characters
Objective: Verify 400 when identityKey exceeds the maximum length
Request: Submit a request with an identityKey longer than 1000 characters
Expected Response (400):
{
"message": "identityKey exceeds maximum length of 1000 characters"
}
3.5 identityKey Not Valid Base64
Objective: Verify 400 when identityKey is not a valid Base64-encoded string
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "!!!not-base64!!!",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Invalid identityKey encoding"
}
3.6 Missing storeId
Objective: Verify 400 when storeId is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Missing required field: storeId"
}
3.7 storeId Exceeds 255 Characters
Objective: Verify 400 when storeId exceeds the maximum length
Request: Submit a request with a storeId longer than 255 characters
Expected Response (400):
{
"message": "storeId exceeds maximum length of 255 characters"
}
3.8 Invalid storeId
Objective: Verify 400 when storeId does not match any known store
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "NONEXISTENT-STORE",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Invalid storeId"
}
3.9 Missing authEvent
Objective: Verify 400 when the entire authEvent object is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001"
}
Expected Response (400):
{
"message": "Missing required field: authEvent"
}
3.10 Missing authEvent.id
Objective: Verify 400 when authEvent.id is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Missing required field: authEvent.id"
}
3.11 Invalid authEvent.id Format
Objective: Verify 400 when authEvent.id does not match UUID format
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "not-a-uuid",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Invalid authEvent.id format"
}
3.12 Missing authEvent.timestamp
Objective: Verify 400 when authEvent.timestamp is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Missing required field: authEvent.timestamp"
}
3.13 Invalid authEvent.timestamp Format
Objective: Verify 400 when authEvent.timestamp is not valid UTC format
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "not-a-timestamp",
"location": "ENTRY"
}
}
Expected Response (400):
{
"message": "Invalid authEvent.timestamp format"
}
3.14 Missing authEvent.location
Objective: Verify 400 when authEvent.location is omitted
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z"
}
}
Expected Response (400):
{
"message": "Missing required field: authEvent.location"
}
3.15 Invalid authEvent.location Value
Objective: Verify 400 when authEvent.location is not ENTRY or EXIT
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "INVALID"
}
}
Expected Response (400):
{
"message": "Invalid authEvent.location value"
}
3.16 Invalid keyChannel Value
Objective: Verify 400 when keyChannel is not OPTICAL or RADIO
Request:
{
"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"identityKey": "dmFsaWRfa2V5",
"storeId": "STORE-001",
"authEvent": {
"id": "11111111-2222-3333-4444-555555555555",
"timestamp": "2024-02-09T18:00:00Z",
"location": "ENTRY"
},
"keyChannel": "BLUETOOTH"
}
Expected Response (400):
{
"message": "Invalid keyChannel value"
}
3.17 Empty Request Body
Objective: Verify 400 when the request body is empty
Request:
{}
Expected Response (400):
{
"message": "Invalid request body"
}
4. Rate Limiting (429)
4.1 Exceed Rate Limit
Objective: Trigger "Too Many Requests" by exceeding the API rate limit
Test Method: Send rapid concurrent requests to the API endpoint beyond the configured rate limit
Expected Response (429):
{
"message": "Too Many Requests"
}
Expected Result: Gate remains closed. Subsequent requests within the rate window are rejected.
5. Internal Server Error (500)
5.1 Server Error — Default Gate Decision
Objective: Verify the gate follows the store's default gate decision when a 500 error occurs
Test Method: Simulate an internal server error (e.g., identity backend unavailable)
Expected Response (500):
{
"message": "Internal server error"
}
Expected Result: Gate follows the configured default gate decision for the store (open or closed depending on store configuration).
6. Idempotency
6.1 Duplicate Request — Same authEvent.id
Objective: Verify the API returns the same response when called multiple times with the same authEvent.id
Test Steps:
- Send a valid request with authEvent.id
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee - Confirm 200 response with visitorDetails
- Send the exact same request again with the same authEvent.id
- Confirm the response is identical to step 2
Expected Result: Both responses are identical. No duplicate shopper sessions are created.
6.2 Duplicate Request — Same authEvent.id, Different requestId
Objective: Verify idempotency is keyed on authEvent.id, not requestId
Test Steps:
- Send a valid request with authEvent.id
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeeeand requestId11111111-... - Send the same request but with a new requestId
22222222-...and the same authEvent.id - Confirm both responses are identical
Expected Result: The authEvent.id drives idempotency. Different requestIds with the same authEvent.id produce the same result.
6.3 Rapid Retries — Same Scan Event
Objective: Simulate Amazon retrying the same scan event multiple times in quick succession
Test Steps:
- Send 5 identical requests with the same authEvent.id within 1 second
- Verify all responses are identical
- Verify only one shopper session is created
Expected Result: All responses are consistent. No side effects from repeated calls.
7. QR Code / Scan Key Code Validation
7.1 Valid Scan Key Code
Objective: Verify a properly formatted Scan Key Code is accepted
Test Steps:
- Generate a Scan Key Code with valid prefix (JWO), customer prefix, recognition token (32 chars), and timestamp
- Base64-encode the payload and submit as identityKey
- Confirm 200 response
Expected Result: Gate opens.
7.2 Scan Key Code with Invalid Prefix
Objective: Verify rejection when the Scan Key Code does not start with JWO
Test Steps:
- Generate a Scan Key Code with prefix "ABC" instead of "JWO"
- Base64-encode and submit
Expected Result: 401 response. Gate remains closed.
7.3 Scan Key Code Below Minimum Length
Objective: Verify rejection when the decoded Scan Key Code is shorter than 49 characters
Test Steps:
- Create a payload shorter than 49 characters
- Base64-encode and submit
Expected Result: 401 response. Gate remains closed.
7.4 Scan Key Code Exceeds Maximum Length
Objective: Verify rejection when the decoded Scan Key Code exceeds 154 characters
Test Steps:
- Create a payload longer than 154 characters
- Base64-encode and submit
Expected Result: 401 response. Gate remains closed.
7.5 Scan Key Code with Expired Timestamp
Objective: Verify rejection when the Scan Key Code timestamp is older than the configured expiry window plus buffer
Test Steps:
- Generate a Scan Key Code with a timestamp older than authEvent.timestamp - expiry window - 15 second buffer
- Base64-encode and submit
Expected Result: 401 response. Gate remains closed.
7.6 Scan Key Code with Non-Alphanumeric Characters
Objective: Verify rejection when the decoded Scan Key Code contains non-alphanumeric characters
Test Steps:
- Create a payload containing special characters (e.g.,
@,#, spaces) - Base64-encode and submit
Expected Result: 401 response. Gate remains closed.
8. Gate Behavior Validation
8.1 Gate Remains Closed on 400
Objective: Confirm the gate does not open when the API returns 400
Test Steps:
- Send a request with missing required fields
- Confirm 400 response
- Verify gate remains closed
Expected Result: Gate remains closed.
8.2 Gate Remains Closed on 401
Objective: Confirm the gate does not open when the API returns 401
Test Steps:
- Send a request with an invalid identity key
- Confirm 401 response
- Verify gate remains closed
Expected Result: Gate remains closed.
8.3 Gate Follows Default Decision on 500
Objective: Confirm the gate follows the store's default gate decision on 500
Test Steps:
- Simulate a 500 error
- Verify gate behavior matches the store's configured default (open or closed)
Expected Result: Gate follows default gate decision.
8.4 Gate Remains Closed During Retries
Objective: Confirm the gate does not open while Amazon is retrying a failed request
Test Steps:
- Simulate a transient failure that triggers retries
- Verify the gate remains closed until a definitive 200 or rejection is received
Expected Result: Gate remains closed until a final authorization decision is made.
9. Performance
9.1 Response Time Under 2 Seconds
Objective: Verify the Identity Connector responds within the 2-second SLA
Test Steps:
- Send 100 valid requests sequentially
- Measure response time for each
- Verify p99 response time is under 2 seconds
Expected Result: All responses return within 2 seconds.
9.2 Concurrent Load
Objective: Verify performance under peak concurrent scan volume
Test Steps:
- Send 50 concurrent valid requests
- Measure response times and success rates
- Verify no timeouts or dropped requests
Expected Result: All requests succeed within the 2-second SLA.
Test Data Requirements
| Data Item | Description |
|---|---|
| Valid shopper identity key | Base64-encoded key mapped to a known SHOPPER |
| Valid associate identity key | Base64-encoded key mapped to a known ASSOCIATE |
| Valid cash shopper identity key | Base64-encoded key mapped to a known CASH_SHOPPER (cash-enabled store only) |
| Invalid identity key | Base64-encoded key that does not map to any shopper |
| Expired identity key | Base64-encoded Scan Key Code with an expired timestamp |
| Flagged shopper identity key | Base64-encoded key mapped to a flagged account |
| Valid store IDs | At least 2 store IDs assigned during onboarding |
| Invalid store ID | A store ID that does not exist |
| Cash-enabled store ID | A store ID that supports CASH_SHOPPER type |
Test Execution Notes
- Base64 Encoding: All identityKey values must be Base64-encoded before submission. Decode before performing validation logic.
- UUID Format: Both requestId and authEvent.id must follow the pattern
[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12} - Idempotency: Coordinate with your team to verify that repeated calls with the same authEvent.id do not create duplicate sessions.
- CASH_SHOPPER: Confirm with the Amazon team whether your store supports the CASH_SHOPPER visitor type before running test 1.3.
- Default Gate Decision: Confirm your store's default gate decision configuration before running test 8.3.
- Latency Buffer: When testing Scan Key Code timestamp validation, account for the recommended 15-second buffer for network delays.

