Skip to main content
The Compute endpoints evaluate arithmetic operations on CKKS ciphertexts (approximate FHE for real numbers). Best for ML inference, statistics, and any “real number” workload. For Boolean gates over TLWE, see TFHE API instead.

POST /api/v1/compute/{operation}

Run a single FHE operation. Path-based variant. Auth: TRIAL+

Request

POST /api/v1/compute/add
Authorization: Bearer wvs_live_<key>
Content-Type: application/json

{
  "key_id": "key_a1b2c3",
  "ciphertext_ids": ["ct_x", "ct_y"]
}
URL operationDescription
addElement-wise addition
multiplyElement-wise multiplication
add-plainAdd a plaintext vector
multiply-plainMultiply by a plaintext vector
negateElement-wise negation
rescaleModulus reduction (consumes a level)
rotateCyclic rotation of slots
matmulMatrix-vector multiplication
poly-evalEvaluate a polynomial on ciphertext
bootstrapRefresh noise budget (≥CKKS depth limit)

Body schema

FieldTypeRequiredNotes
key_idstringYesFrom /keys/upload or /keys/generate
ciphertext_idsarray<string>Yes1 element for unary, 2 for binary
plain_operandarray<float>For *-plain opsPlaintext vector, length = slot_count
polynomial_coeffsarray<float>For poly-evalCoefficients (low→high), max 65
scale_stableboolNoEnables scale-stable multiplication

Response — 200 OK

{
  "job_id": "op_a1b2c3",
  "status": "COMPLETED",
  "result_ciphertext_id": "ct_result_xyz",
  "result_ciphertext": "<base64>",
  "compute_time_ms": 8.2,
  "noise_budget_remaining": 78.5,
  "evicted_ciphertext_ids": [],
  "cost_units": 5.0,
  "cost_jpy": 0.50,
  "bootstrap_applied": false,
  "bootstrap_effective_degree": null,
  "bootstrap_degraded": false
}

Response headers

HeaderDescription
X-Noise-Budget-RemainingSame as noise_budget_remaining field (% of noise budget left)
X-Noise-Budget-Warning"true" if remaining <50% — bootstrap soon
X-Noise-Budget-RecoveryRecovery hint, e.g. "POST /api/v1/compute/bootstrap to refresh"
When the noise budget is exhausted, further operations on the resulting ciphertext will fail decryption. Bootstrap before that happens.

POST /api/v1/compute

Generic variant — operation in the body instead of the path.

Request

POST /api/v1/compute
{
  "key_id": "key_a1b2c3",
  "operation": "matmul",
  "ciphertext_ids": ["ct_matrix", "ct_vector"]
}
Same response as path-based variant.

FCU costs (per operation)

Each operation has a FHE Compute Unit weight that determines billing under PAYG and quota consumption under fixed-tier plans.
OperationFCU weightPAYG cost (USD)
add, negate, rescale, add-plain1$0.000002
multiply-plain2$0.000004
multiply, rotate, key_switch5$0.000010
poly-eval10$0.000020
matmul20$0.000040
bootstrap50$0.000100
infer_7b100$0.000200
infer_70b1000$0.002000
cost_jpy in the response is the FCU weight × current JPY rate.

Operation specifics

add and multiply

Element-wise. Both ciphertexts must:
  • Be encrypted under the same key_id
  • Have the same number of slots
  • Be at the same modulus level (or use rescale first)
POST /api/v1/compute/multiply
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_a", "ct_b"],
  "scale_stable": true
}
scale_stable: true keeps the result at the same scale as the inputs (auto-rescale). Recommended unless you’re managing the modulus chain manually.

add-plain and multiply-plain

Mix plaintext into a ciphertext.
POST /api/v1/compute/multiply-plain
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_x"],
  "plain_operand": [0.5, 0.5, 0.5, 0.5]
}
plain_operand.length must equal slot_count.

poly-eval

Evaluate a polynomial (Horner-style) on a ciphertext. Useful for activation functions in encrypted ML.
POST /api/v1/compute/poly-eval
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_x"],
  "polynomial_coeffs": [0, 1, 0, -0.16667, 0, 0.00833]
}
Above evaluates sin(x) ≈ x - x³/6 + x⁵/120 over the encrypted vector. polynomial_coeffs.length ≤ 65. Higher-degree polynomials should be decomposed into multiple poly-eval calls or use a Chebyshev expansion.

matmul

Matrix-vector multiply. The matrix is encoded as slot_count rotated copies of the vector (BSGS — baby-step-giant-step optimization).
POST /api/v1/compute/matmul
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_matrix_packed", "ct_vector"]
}
For non-square or larger matrices, decompose into block-wise matmuls.

rotate

Cyclic rotation of slots — needed for matmul, dot-product, and any inter-slot data movement.
POST /api/v1/compute/rotate
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_x"],
  "rotation_steps": 3
}
(rotation_steps is in plain_operand[0] if your client puts it there.)

bootstrap

Refresh the noise budget so the ciphertext can support more operations. Expensive (~500 ms for poly_degree=8192) but unavoidable in deep circuits.
POST /api/v1/compute/bootstrap
{
  "key_id": "key_a1",
  "ciphertext_ids": ["ct_x"]
}
Response includes:
  • bootstrap_effective_degree: residual depth after bootstrap
  • bootstrap_degraded: true if precision dropped below threshold

GET /api/v1/compute/{op_id}

Poll status for an asynchronously running operation. Auth: WRITE+

Response — 200 OK

{
  "op_id": "op_a1b2c3",
  "status": "running"
}
status: "pending", "running", "completed", "cancelled", "failed". For completed operations, the full response is in the original POST response (not re-fetchable via GET).

DELETE /api/v1/compute/{op_id}

Cancel a pending or running operation. Best-effort — already-completed work isn’t refunded. Auth: WRITE+

Response — 200 OK

{
  "op_id": "op_a1b2c3",
  "cancelled": true
}

Common errors

CodeStatusMeaning
KEY_NOT_FOUND404key_id invalid
CIPHERTEXT_NOT_FOUND404One of ciphertext_ids missing or evicted
MISMATCHED_KEY400Ciphertexts under different keys
MISMATCHED_LEVEL400Ciphertexts at different modulus levels (use rescale)
NOISE_BUDGET_EXHAUSTED400Result would fail decryption — bootstrap first
INVALID_OPERATION400operation not recognized
MISSING_PLAIN_OPERAND400*-plain op without plain_operand
POLYNOMIAL_TOO_LARGE400More than 65 coefficients

Best practices

  1. Track noise budget. Read X-Noise-Budget-Remaining and bootstrap when it dips below 30%.
  2. Batch independent ops at the application level — multiple parallel /compute requests with HTTP/2 multiplexing.
  3. Use scale_stable: true unless you’re explicitly managing the modulus chain.
  4. Prefer multiply-plain over multiply when one operand is public — 2× cheaper.
  5. Cache ciphertext_ids client-side. Avoid re-uploading.

Next Steps

Performance

CKKS performance tuning

Webhooks

Async events for long-running compute