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

HeaderRequiredDescription
AuthorizationYesBearer token with API key
Content-TypeYesMust be application/json
X-Request-IdNoClient-provided request ID for tracing

Body

{
  "payload": { ... },
  "policyNumber": "POLICY-123"
}
FieldTypeRequiredDescription
payloadanyYesThe plaintext data to protect (any JSON-serializable value)
policyNumberstringNoThe 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..."
  }
}
FieldTypeDescription
requestIdstringUnique request identifier for tracing
messageTypestringAlways "ProtectedPayload"
operationsstring[]Operations performed (e.g. ["encrypt"])
jweobjectJWE 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.

FieldDescription
protectedBase64url-encoded JOSE header
encrypted_keyBase64url-encoded encrypted content encryption key
ivBase64url-encoded initialization vector
ciphertextBase64url-encoded ciphertext
tagBase64url-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

  1. Use request IDs — Pass X-Request-Id for end-to-end tracing
  2. Handle errors gracefully — Different error codes require different handling
  3. Don't parse the JWE — Treat the JWE envelope as opaque
  4. Log the requestId — For debugging with audit logs