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

HeaderRequiredDescription
AuthorizationYesBearer token with API key
Content-TypeYesMust be application/json
X-Request-IdNoClient-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"
}
FieldTypeRequiredDescription
jweobjectOne of jwe or protectedPayloadJWE Flattened JSON envelope to decrypt
protectedPayloadstringOne of jwe or protectedPayloadLegacy base64url-encoded protected payload
policyNumberstringNoThe policy number that defines how to unprotect. If omitted, uses the policy bound to the API key.

JWE Envelope Fields

FieldTypeDescription
protectedstringBase64url-encoded JOSE header
encrypted_keystringBase64url-encoded encrypted content encryption key
ivstringBase64url-encoded initialization vector
ciphertextstringBase64url-encoded ciphertext
tagstringBase64url-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": [...]
  }
}
FieldTypeDescription
requestIdstringUnique request identifier for tracing
operationsstring[]Operations performed (e.g. ["decrypt"])
payloadanyThe 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:

  1. Policy references the destination service
  2. Destination service has associated key pairs
  3. 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

  1. Match policies — Use the corresponding unprotect policy for protected payloads
  2. Log request IDs — For debugging with audit logs
  3. Don't retry on crypto errors — Decryption failures won't succeed on retry
  4. Prefer JWE format — Use the jwe field for new integrations; protectedPayload is legacy