POST /unprotect
API reference for the unprotect endpoint - decrypt JWE payloads
The /unprotect endpoint accepts a JWE envelope (or legacy protected payload) and returns the original plaintext according to the specified policy.
Endpoint
POST /api/v1/unprotect
Request
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token with API key |
Content-Type | Yes | Must be application/json |
X-Request-Id | No | Client-provided request ID for tracing |
Body
Provide either jwe (preferred) or protectedPayload (legacy format):
{
"jwe": {
"protected": "eyJhbGci...",
"encrypted_key": "OKOawDo13gk...",
"iv": "GZCSp3OrqWc...",
"ciphertext": "a7CcGy23OO...",
"tag": "Mhf5r127LQn..."
},
"policyNumber": "POLICY-123"
}
| Field | Type | Required | Description |
|---|---|---|---|
jwe | object | One of jwe or protectedPayload | JWE Flattened JSON envelope to decrypt |
protectedPayload | string | One of jwe or protectedPayload | Legacy base64url-encoded protected payload |
policyNumber | string | No | The policy number that defines how to unprotect. If omitted, uses the policy bound to the API key. |
JWE Envelope Fields
| Field | Type | Description |
|---|---|---|
protected | string | Base64url-encoded JOSE header |
encrypted_key | string | Base64url-encoded encrypted content encryption key |
iv | string | Base64url-encoded initialization vector |
ciphertext | string | Base64url-encoded ciphertext |
tag | string | Base64url-encoded authentication tag |
Example Request
curl -X POST https://api.4quays.com/api/v1/unprotect \
-H "Authorization: Bearer sk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"jwe": {
"protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
"encrypted_key": "OKOawDo13gk...",
"iv": "GZCSp3OrqWc...",
"ciphertext": "a7CcGy23OO...",
"tag": "Mhf5r127LQn..."
},
"policyNumber": "RBC-STATEMENT-001"
}'
Response
Success (200 OK)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"operations": ["decrypt"],
"payload": {
"statementId": "stmt-12345",
"accountNumber": "CA1234567890",
"balance": 25000.50,
"transactions": [...]
}
}
| Field | Type | Description |
|---|---|---|
requestId | string | Unique request identifier for tracing |
operations | string[] | Operations performed (e.g. ["decrypt"]) |
payload | any | The original plaintext data |
Error Responses
400 Bad Request
{
"error": {
"code": "INVALID_REQUEST",
"message": "Request validation failed",
"details": [...]
},
"requestId": "req_abc123"
}
Returned when neither jwe nor protectedPayload is provided, or when the request body is not valid JSON.
400 Bad Request (Invalid Protected Payload)
{
"error": {
"code": "INVALID_PROTECTED_PAYLOAD",
"message": "Invalid protected payload format"
},
"requestId": "req_abc123"
}
Returned when the legacy protectedPayload string cannot be parsed.
400 Bad Request (Unprotect Not Available)
{
"error": {
"code": "UNPROTECT_NOT_AVAILABLE",
"message": "Unprotect is not available for destination-managed key policies"
},
"requestId": "req_abc123"
}
Returned when attempting to unprotect a JWE envelope for a policy where the key is managed by the destination service (the private key is not held by 4Quays).
401 Unauthorized
{
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid or missing API key"
},
"requestId": "req_abc123"
}
404 Not Found
{
"error": {
"code": "POLICY_NOT_FOUND",
"message": "Policy not found: INVALID-POLICY"
},
"requestId": "req_abc123"
}
500 Internal Server Error
{
"error": {
"code": "DESTINATION_KEY_NOT_FOUND",
"message": "Destination service private key not found"
},
"requestId": "req_abc123"
}
Returned when the policy's destination service has no active private key configured.
{
"error": {
"code": "INTERNAL_ERROR",
"message": "Unprotection operation failed"
},
"requestId": "req_abc123"
}
Usage Example
const response = await fetch('https://api.4quays.com/api/v1/unprotect', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
jwe: receivedJweEnvelope,
policyNumber: 'RBC-STATEMENT-001',
}),
});
const data = await response.json();
if (!response.ok) {
console.error('Unprotect failed:', data.error.code, data.error.message);
throw new Error(data.error.message);
}
console.log('Balance:', data.payload.balance);
console.log('Transactions:', data.payload.transactions);
Key Resolution
4Quays uses the policy to determine which key to use for decryption:
- Policy references the destination service
- Destination service has associated key pairs
- The active private key is selected for decryption
For policies where the key is managed by the destination (destination_managed key ownership), unprotect using the JWE format is not available — the private key is held by the external service, not by 4Quays.
Best Practices
- Match policies — Use the corresponding unprotect policy for protected payloads
- Log request IDs — For debugging with audit logs
- Don't retry on crypto errors — Decryption failures won't succeed on retry
- Prefer JWE format — Use the
jwefield for new integrations;protectedPayloadis legacy
Related
- POST /protect — Reverse operation
- Error Codes — Full error reference