POST /protect
API reference for the protect endpoint - encrypt payloads using JWE
The /protect endpoint accepts a plaintext payload and returns a JWE (JSON Web Encryption) envelope according to the specified policy.
Endpoint
POST /api/v1/protect
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
{
"payload": { ... },
"policyNumber": "POLICY-123"
}
| Field | Type | Required | Description |
|---|---|---|---|
payload | any | Yes | The plaintext data to protect (any JSON-serializable value) |
policyNumber | string | No | The policy number that defines how to protect. If omitted, uses the policy bound to the API key. |
Example Request
curl -X POST https://api.4quays.com/api/v1/protect \
-H "Authorization: Bearer sk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"transferId": "txn-12345",
"amount": 5000,
"currency": "CAD",
"destination": "CA1234567890"
},
"policyNumber": "RBC-TRANSFER-001"
}'
Response
Success (200 OK)
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"messageType": "ProtectedPayload",
"operations": ["encrypt"],
"jwe": {
"protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
"encrypted_key": "OKOawDo13gk...",
"iv": "GZCSp3OrqWc...",
"ciphertext": "a7CcGy23OO...",
"tag": "Mhf5r127LQn..."
}
}
| Field | Type | Description |
|---|---|---|
requestId | string | Unique request identifier for tracing |
messageType | string | Always "ProtectedPayload" |
operations | string[] | Operations performed (e.g. ["encrypt"]) |
jwe | object | JWE Flattened JSON Serialization (RFC 7516) |
JWE Envelope Structure
The jwe object follows the JWE Flattened JSON Serialization format. Your application should treat this as opaque — do not parse or modify it.
| Field | Description |
|---|---|
protected | Base64url-encoded JOSE header |
encrypted_key | Base64url-encoded encrypted content encryption key |
iv | Base64url-encoded initialization vector |
ciphertext | Base64url-encoded ciphertext |
tag | Base64url-encoded authentication tag |
Passthrough Response
When a policy is inactive and its failure mode is set to passthrough, the payload is returned unencrypted with a warning:
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"messageType": "ProtectedPayload",
"operations": [],
"payload": { "transferId": "txn-12345", "amount": 5000 },
"warning": "Policy is not active — payload passed through unprotected"
}
Drop Response
When a policy is inactive and its failure mode is set to drop, the payload is discarded:
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"messageType": "ProtectedPayload",
"operations": [],
"dropped": true,
"message": "Policy is not active — payload dropped"
}
Error Responses
400 Bad Request
{
"error": {
"code": "INVALID_REQUEST",
"message": "Request validation failed",
"details": [...]
},
"requestId": "req_abc123"
}
401 Unauthorized
{
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid or missing API key"
},
"requestId": "req_abc123"
}
403 Forbidden
{
"error": {
"code": "POLICY_NOT_ACTIVE",
"message": "Policy is not active"
},
"requestId": "req_abc123"
}
Returned when the policy is inactive and its failure mode is error (the default).
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 public key not found"
},
"requestId": "req_abc123"
}
Returned when the policy's destination service has no active public key configured.
{
"error": {
"code": "INTERNAL_ERROR",
"message": "Protection operation failed"
},
"requestId": "req_abc123"
}
Usage Example
const response = await fetch('https://api.4quays.com/api/v1/protect', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
payload: {
transferId: 'txn-12345',
amount: 5000,
currency: 'CAD',
destination: 'CA1234567890',
},
policyNumber: 'RBC-TRANSFER-001',
}),
});
const data = await response.json();
if (!response.ok) {
console.error('Protect failed:', data.error.code, data.error.message);
throw new Error(data.error.message);
}
// Send the JWE envelope to the external service
await sendToBank(data.jwe);
Payload Types
The payload field accepts any JSON-serializable value:
// Object
{ payload: { key: 'value' } }
// Array
{ payload: [1, 2, 3] }
// String
{ payload: 'sensitive-data' }
// Number
{ payload: 12345 }
// Nested structures
{ payload: { user: { id: 1 }, items: [{ id: 1 }] } }
Policy Behavior
The policy determines the encryption algorithm and destination key used to protect the payload. All policies currently produce encrypted output using hybrid encryption (symmetric cipher + asymmetric key wrapping).
If policyNumber is omitted from the request, the API uses the policy bound to the authenticated API key.
Best Practices
- Use request IDs — Pass
X-Request-Idfor end-to-end tracing - Handle errors gracefully — Different error codes require different handling
- Don't parse the JWE — Treat the JWE envelope as opaque
- Log the requestId — For debugging with audit logs
Related
- POST /unprotect — Reverse operation
- Error Codes — Full error reference