Pular para o conteúdo principal

Encrypted File Ingestion via an External Decryption Service

Automation Anywhere Enterprise Knowledge Base (AAEKB / EKB) — Administrator and Integration Guide

What this feature does​

AAEKB can hand an uploaded file to an external decryption service that you host and control, receive the decrypted bytes back, and then run its normal Knowledge Base ingestion pipeline (text extraction, OCR, chunking, embedding, indexing) on the decrypted content.

This exists for organisations that apply enterprise DRM or envelope encryption to documents at rest and cannot ship decryption keys to a third-party application. The keys, the decryption logic, and the plaintext boundary all stay inside your network. AAEKB only knows how to call an HTTP endpoint and read a byte stream back.

The integration is deliberately generic and configuration-driven. There is no vendor-specific DRM SDK inside AAEKB. Any HTTP service that accepts a file and returns a file — synchronously or via a job/poll pattern — can be plugged in, provided it fits the contract in The contract your decryption service must implement.

Key characteristics:

  • Opt-in per upload. Files are only sent to the decryption service when the user explicitly ticks a toggle. Normal uploads are completely unaffected.
  • Single global configuration. One decryption service is configured per AAEKB deployment, by a Super Admin.
  • In-memory only. The encrypted bytes, the request to your service, and the decrypted response are all handled in memory. AAEKB does not write the ciphertext to a temporary file on disk before calling your service.
  • No key material in AAEKB. AAEKB stores an endpoint URL and, optionally, an API credential for calling your service. It never stores or handles document encryption keys.

How it works end to end​

User (browser) AAEKB backend Your decryption service
| | |
| 1. Upload file + | |
| "Requires Decryption" | |
|----------------------------->| |
| | |
| 2. File security validation |
| (runs on the ENCRYPTED bytes) |
| | |
| | 3. POST encrypted file |
| |--------------------------------->|
| | |
| | 4a. sync: 200 + decrypted bytes |
| |<---------------------------------|
| | |
| | 4b. async: 202 + { job_id } |
| |<---------------------------------|
| | 5b. GET poll/{job_id} (repeat) |
| |--------------------------------->|
| | 6b. 200 + decrypted bytes |
| |<---------------------------------|
| | |
| 7. MIME type re-detected on decrypted bytes |
| 8. Decrypted file stored in KB raw storage |
| 9. Normal ingestion: extract, chunk, embed, index |
| | |
| 10. Resource appears in KB | |
|<-----------------------------| |

Step by step:

  1. Upload. The user selects files in the Knowledge Base upload modal and enables the Requires Decryption toggle. The frontend sends needs_decryption=true as an extra form field alongside the file.
  2. File security validation. AAEKB's standard upload security middleware runs before decryption, against the encrypted bytes and the original filename. This is the single most important compatibility constraint — see Compatibility.
  3. Submit to your service. AAEKB reads the file into memory and sends it to your configured decrypt endpoint.
  4. Receive the plaintext. Either immediately (sync mode) or after polling a result endpoint (async mode).
  5. Re-detect the file type. AAEKB re-derives the MIME type from the decrypted bytes: first from the filename extension, then, if that yields nothing, by sniffing the first 2 KB of the decrypted content.
  6. Store and ingest. The decrypted file is written to the Knowledge Base raw file storage and then processed by the ordinary ingestion pipeline. Decryption metadata is merged into the document's metadata record.

Two upload paths exist and both support the feature identically:

PathEndpointWhere decryption runs
Synchronous upload (legacy / Quick Upload)POST /v3/project/knowledge/add/fileInside the web request
Background upload (default)POST /v5/project/knowledge/add/fileInside a background worker process

The practical implication: both your web/API containers and your background worker containers must be able to reach the decryption service.

Configuring the service (Super Admin)​

  1. Sign in as a Super Admin.
  2. Open Super Admin → Platform & integrations → KB Decryption Service.
  3. Fill in the configuration described in Full configuration reference.
  4. Toggle Enabled on.
  5. Click Save Changes.

Notes on the admin screen:

  • The Async Polling Configuration card is only shown when Response Mode is set to Asynchronous.
  • The auth credential field is masked when the configuration is read back. Only the first four characters are shown; the rest is replaced with bullet characters. Re-saving without touching the field leaves the stored credential unchanged. To rotate the credential, clear the field and type the new value in full.
  • Saving is atomic across the whole form — there is no per-field save.
  • Setting Enabled to off immediately hides the end-user toggle and causes any upload that still claims needs_decryption=true to be rejected with a clear error.

The same configuration is available over the REST API for automated provisioning:

MethodPathAccessPurpose
GET/admin/kb-decryption-serviceSuper AdminRead configuration (credential masked)
PUT/admin/kb-decryption-serviceSuper AdminPartial update; only the fields you send are changed
GET/kb-decryption-service/statusAny authenticated userReturns {"available": true}

Example provisioning call:

curl -X PUT "https://ekb.example.com/admin/kb-decryption-service" \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"display_name": "Corporate DRM Gateway",
"description": "Intranet decryption service for classified documents",
"decrypt_endpoint": "https://drm.corp.internal/api/v1/async/decrypt/stream",
"decrypt_http_method": "POST",
"decrypt_content_type": "multipart/form-data",
"decrypt_file_field_name": "file",
"decrypt_extra_headers": {"X-Tenant": "aa-ekb"},
"decrypt_extra_body_fields": {"purpose": "ingestion"},
"response_mode": "async",
"poll_endpoint_template": "https://drm.corp.internal/api/v1/async/download/stream/{job_id}",
"poll_interval_seconds": 5,
"poll_max_attempts": 120,
"poll_job_id_json_path": "job_id",
"poll_status_json_path": "status",
"poll_completed_status": "COMPLETED",
"poll_failed_statuses": "FAILED,ERROR",
"auth_type": "api_key_header",
"auth_token": "s3cr3t-api-key",
"auth_header_name": "X-API-Key"
}'

Full configuration reference​

General​

FieldTypeDefaultMeaning
enabledbooleanfalseMaster switch. When off, the end-user toggle is hidden and decryption requests are refused.
display_namestringDefault Decryption ServiceFriendly name. Recorded in each decrypted document's metadata.
descriptionstring(empty)Free-text note for administrators. Not used at runtime.

Decryption request​

FieldTypeDefaultMeaning
decrypt_endpointURL(empty)Absolute URL AAEKB posts the encrypted file to. Must be reachable from both API and worker containers.
decrypt_http_methodPOST | PUTPOSTHTTP verb used for the submit request.
decrypt_content_typemultipart/form-data | application/octet-streammultipart/form-dataHow the file is transmitted. See the note below.
decrypt_file_field_namestringfileMultipart form field name carrying the file. Only used in multipart mode.
decrypt_extra_headersJSON object{}Extra headers merged into both the submit request and every poll request.
decrypt_extra_body_fieldsJSON object{}Extra form fields sent alongside the file. Only used in multipart mode.
response_modesync | asyncasyncWhether the decrypted bytes come back on the submit call or via polling.
aviso

application/octet-stream mode. When content type is application/octet-stream, AAEKB sends the raw file bytes as the entire request body. In this mode the filename is not transmitted and decrypt_extra_body_fields is ignored, because there is no multipart envelope to carry them. If your service needs the filename or additional context, either use multipart/form-data or pass the context through decrypt_extra_headers.

Async polling (only used when response_mode = async)​

FieldTypeDefaultMeaning
poll_endpoint_templateURL template(empty)Result URL. Must contain the literal placeholder {job_id}, which is substituted with the extracted job identifier.
poll_interval_secondsinteger5Seconds to wait between polls. AAEKB sleeps before the first poll, so the minimum decryption latency is one interval.
poll_max_attemptsinteger120Maximum number of polls. Total budget = poll_interval_seconds × poll_max_attempts (default 600 s / 10 minutes).
poll_job_id_json_pathdot pathjob_idWhere to find the job identifier in the submit response, e.g. data.jobId.
poll_status_json_pathdot pathstatusWhere to find the job status in a JSON poll response, e.g. result.state.
poll_completed_statusstringCOMPLETEDStatus value meaning "done". Compared case-insensitively.
poll_failed_statusescomma-separatedFAILED,ERRORStatus values that abort the job immediately. Compared case-insensitively.

Dot paths are simple nested-dictionary lookups. data.job.id resolves response["data"]["job"]["id"]. Array indexing is not supported — a path segment cannot be a list index.

Authentication​

FieldTypeDefaultMeaning
auth_typenone | bearer | api_key_header | custom_headersnoneAuthentication scheme. See Authentication options.
auth_tokensecret string(empty)The bearer token or API key value. Masked on read.
auth_header_namestringAuthorizationHeader name used when auth_type = api_key_header.

The contract your decryption service must implement​

This is the specification to hand to whoever builds or operates the decryption service.

Non-negotiable requirements​

  1. Speak HTTP(S). One endpoint that accepts a file; in async mode, a second endpoint that returns the result for a job.
  2. Return the decrypted file as raw bytes, not as base64, not wrapped in JSON, not as a redirect to a download URL. AAEKB streams the response body straight into the ingestion pipeline.
  3. Preserve the file's original format. If the ciphertext decrypts to a PDF, return a well-formed PDF. AAEKB does no format conversion.
  4. Return a non-empty body on success. An empty response is treated as a failure.
  5. Be reachable from AAEKB's API and worker containers, with a TLS certificate those containers trust.
  6. Be idempotent enough to survive a repeated GET on the poll endpoint. See The double-GET behaviour.

Submit endpoint​

Multipart mode (recommended). AAEKB sends:

  • Method: as configured (POST or PUT)
  • Content-Type: multipart/form-data with a generated boundary
  • One file part, named by decrypt_file_field_name, with filename set to the original upload filename
  • Zero or more additional form fields from decrypt_extra_body_fields
  • Auth and extra headers as configured

Octet-stream mode. AAEKB sends the raw bytes as the request body with Content-Type: application/octet-stream. No filename, no extra body fields.

Expected responses:

ModeAcceptable statusBody
sync200 onlyDecrypted file bytes
async202 (typical) or 200JSON containing a job identifier, or — if 200 with a binary content type — the decrypted bytes directly

In async mode AAEKB inspects the submit response before deciding whether to poll:

  • 200 and a binary-looking Content-Type → the bytes are used directly, no polling. (This lets a service short-circuit for small files.)
  • Anything else → the body is parsed as JSON and the job identifier is extracted using poll_job_id_json_path.

A "binary-looking" content type is one that contains octet-stream, or that starts with application/ and does not contain json. So application/octet-stream and application/pdf are treated as binary; application/json is not. text/plain is not treated as binary — see Content-Type guidance.

Poll endpoint (async mode)​

AAEKB issues a GET to poll_endpoint_template with {job_id} substituted, carrying the same auth and extra headers as the submit request. It repeats this every poll_interval_seconds, up to poll_max_attempts times.

Your service should respond with one of:

SituationStatusBodyAAEKB behaviour
Still working202Anything (ignored)Waits and polls again
Still working200JSON whose status field is neither the completed nor a failed valueWaits and polls again
Done200Raw decrypted bytes with a binary Content-TypePreferred. Bytes are used; polling stops
Done200JSON whose status field equals poll_completed_statusIssues one more GET to the same URL expecting binary; if that is not binary, the upload fails
Failed200JSON whose status field is in poll_failed_statusesAborts immediately with an error naming the status
Error>= 400AnythingAborts immediately. Does not retry
aviso

Do not return 404 for a job that is merely not ready yet. Any status code of 400 or above ends the job with a hard failure. Use 202 for "not ready".

The double-GET behaviour (async mode)​

If your poll endpoint answers 200 with a JSON body saying the job is complete, AAEKB assumes the file must be fetched separately and immediately issues a second GET to the very same URL, this time expecting a binary stream. If that second call also returns JSON, the upload fails with "Decryption job completed but no file stream was returned."

There are two ways to satisfy this:

  • Preferred: have the poll endpoint return the raw bytes directly with Content-Type: application/octet-stream as soon as the job is done. AAEKB detects this on the first poll and never issues the second request. This is the simplest and fastest design.
  • Acceptable: have the endpoint return a JSON status while pending/complete, and switch to returning the byte stream on a subsequent call. The job must remain retrievable for at least one extra GET after it first reports completion.

What is not supported is a design where the completed JSON contains a different download URL. AAEKB will not follow it.

Content-Type guidance​

ResponseRecommended Content-Type
Decrypted file bytesapplication/octet-stream
Job accepted / job statusapplication/json

Returning the decrypted payload as text/plain or text/csv in async mode will cause AAEKB to try to parse it as a job-status JSON document and fail. In sync mode the content type of the response is not inspected at all — any 200 is taken as the decrypted bytes — but application/octet-stream is still recommended for consistency.

What AAEKB never does​

  • It does not send the decryption key, user credentials, or the end user's identity to your service. If your service needs to authorise per-document or per-user, that context must come from decrypt_extra_headers / decrypt_extra_body_fields, which are static and deployment-wide.
  • It does not retry a failed submit or a failed poll. One failure ends the upload.
  • It does not follow redirects to alternative download hosts as part of the completion handshake.
  • It does not chunk or range-request large files. The whole file goes in one request and comes back in one response.

Worked examples: synchronous mode​

Configuration:

{
"enabled": true,
"decrypt_endpoint": "https://drm.corp.internal/api/v1/decrypt",
"decrypt_http_method": "POST",
"decrypt_content_type": "multipart/form-data",
"decrypt_file_field_name": "file",
"decrypt_extra_body_fields": { "purpose": "ingestion" },
"decrypt_extra_headers": { "X-Tenant": "aa-ekb" },
"response_mode": "sync",
"auth_type": "bearer",
"auth_token": "eyJhbGciOi..."
}

Request AAEKB sends:

POST /api/v1/decrypt HTTP/1.1
Host: drm.corp.internal
Authorization: Bearer eyJhbGciOi...
X-Tenant: aa-ekb
Content-Type: multipart/form-data; boundary=----AAEKBBoundary7MA4YWxk
Content-Length: 2481037

------AAEKBBoundary7MA4YWxk
Content-Disposition: form-data; name="purpose"

ingestion
------AAEKBBoundary7MA4YWxk
Content-Disposition: form-data; name="file"; filename="Q3-Financials.pdf"
Content-Type: application/octet-stream

<...encrypted bytes...>
------AAEKBBoundary7MA4YWxk--

Successful response your service must return:

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 2394112
Content-Disposition: attachment; filename="Q3-Financials.pdf"

%PDF-1.7
<...decrypted bytes...>

Content-Disposition is optional and ignored; AAEKB keeps the original upload filename.

Failure response — any non-200 ends the upload. Include a short, human-readable body; AAEKB surfaces the first 500 characters of it to the end user.

HTTP/1.1 403 Forbidden
Content-Type: application/json

{"error": "Document policy forbids export for this identity"}

The user sees: Decryption service returned HTTP 403: {"error": "Document policy forbids export for this identity"}

Worked examples: asynchronous (polling) mode​

Configuration:

{
"enabled": true,
"decrypt_endpoint": "https://drm.corp.internal/api/v1/async/decrypt/stream",
"decrypt_content_type": "multipart/form-data",
"decrypt_file_field_name": "file",
"response_mode": "async",
"poll_endpoint_template": "https://drm.corp.internal/api/v1/async/download/stream/{job_id}",
"poll_interval_seconds": 5,
"poll_max_attempts": 120,
"poll_job_id_json_path": "job_id",
"poll_status_json_path": "status",
"poll_completed_status": "COMPLETED",
"poll_failed_statuses": "FAILED,ERROR",
"auth_type": "api_key_header",
"auth_token": "s3cr3t-api-key",
"auth_header_name": "X-API-Key"
}

Step 1 — Submit

POST /api/v1/async/decrypt/stream HTTP/1.1
Host: drm.corp.internal
X-API-Key: s3cr3t-api-key
Content-Type: multipart/form-data; boundary=----AAEKBBoundary7MA4YWxk

------AAEKBBoundary7MA4YWxk
Content-Disposition: form-data; name="file"; filename="Q3-Financials.pdf"
Content-Type: application/octet-stream

<...encrypted bytes...>
------AAEKBBoundary7MA4YWxk--

Response:

HTTP/1.1 202 Accepted
Content-Type: application/json

{
"job_id": "8f14e45f-ceea-467a-9f7a-2a1d4b0c9e33",
"status": "PENDING"
}

With a nested identifier you would instead set poll_job_id_json_path to data.job.id and return:

{ "data": { "job": { "id": "8f14e45f-ceea-467a-9f7a-2a1d4b0c9e33" } } }

Step 2 — Poll while pending

AAEKB waits 5 seconds, then:

GET /api/v1/async/download/stream/8f14e45f-ceea-467a-9f7a-2a1d4b0c9e33 HTTP/1.1
Host: drm.corp.internal
X-API-Key: s3cr3t-api-key

Either of these keeps AAEKB waiting:

HTTP/1.1 202 Accepted
Content-Type: application/json

{"status": "PROCESSING", "message": "File is still being processed."}
HTTP/1.1 200 OK
Content-Type: application/json

{"status": "PROCESSING", "progress": 42}

Step 3 — Poll when complete (recommended pattern)

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 2394112
Content-Disposition: attachment; filename="Q3-Financials.pdf"

%PDF-1.7
<...decrypted bytes...>

AAEKB stops polling and proceeds to ingestion.

Step 3 (alternative) — Complete via JSON, then stream

HTTP/1.1 200 OK
Content-Type: application/json

{"status": "COMPLETED"}

AAEKB immediately re-issues the same GET, and your service must then answer with the binary stream shown above. If it answers with JSON again, the upload fails.

Step 3 (failure)

HTTP/1.1 200 OK
Content-Type: application/json

{"status": "FAILED", "reason": "Key not available for principal"}

The user sees: External decryption job failed with status: FAILED

Note that only the status value is surfaced, not your reason field. If you want detail in the user-facing message, encode it into the status string or return a 4xx/5xx with a descriptive body instead.

Reference mock service

A minimal echo-back mock, useful for validating the plumbing before the real service is ready:

# Minimal AAEKB-compatible async decryption mock (FastAPI).
# Returns the uploaded bytes unchanged.
import io, uuid
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import JSONResponse, StreamingResponse

app = FastAPI()
JOBS: dict[str, tuple[bytes, str]] = {}


@app.post("/api/v1/async/decrypt/stream")
async def submit(file: UploadFile = File(...)):
job_id = str(uuid.uuid4())
JOBS[job_id] = (await file.read(), file.filename or "file")
return JSONResponse(status_code=202, content={"job_id": job_id, "status": "PENDING"})


@app.get("/api/v1/async/download/stream/{job_id}")
async def download(job_id: str):
entry = JOBS.pop(job_id, None)
if entry is None:
# Not ready (or unknown). Must NOT be a 4xx — that would abort the job.
return JSONResponse(status_code=202, content={"status": "PROCESSING"})
data, filename = entry
return StreamingResponse(
io.BytesIO(data),
media_type="application/octet-stream",
headers={
"Content-Disposition": f'attachment; filename="{filename}"',
"Content-Length": str(len(data)),
},
)

Authentication options​

auth_typeWhat AAEKB sendsNotes
noneOnly decrypt_extra_headersUse with network-level controls (mTLS terminated at a proxy, IP allowlist, service mesh).
bearerAuthorization: Bearer <auth_token>Plus decrypt_extra_headers.
api_key_header<auth_header_name>: <auth_token>Plus decrypt_extra_headers. Typical: X-API-Key.
custom_headersOnly decrypt_extra_headersPut the complete header set, including any credential, in the extra headers JSON. Use for signature schemes or multi-header auth.

In every mode, decrypt_extra_headers is merged in, and the resulting header set is applied to both the submit request and every poll request.

Not supported natively: OAuth 2.0 client-credentials flows with token refresh, HMAC request signing, and mutual TLS with a client certificate presented by AAEKB. All three can be handled by placing a small authenticating reverse proxy or sidecar between AAEKB and the decryption service, and pointing decrypt_endpoint at the proxy. Static tokens configured here do not auto-refresh; a rotating credential must be re-saved in the admin screen or pushed via the PUT API.

End-user experience​

Visibility. The Knowledge Base upload modals call GET /kb-decryption-service/status when they open. The Requires Decryption toggle is rendered only when that returns {"available": true}, which requires the configuration row to exist and enabled to be true. Users of a deployment without a configured service never see it. The status result is cached briefly in the browser (about 30 seconds), so enabling or disabling the feature may take up to that long to appear or disappear in an already-open session.

Where it appears. Both KB upload entry points expose the toggle:

  • The Add Resources modal (drag-and-drop / file picker within a Knowledge Base)
  • The Selected Files modal (used when files are dropped onto the KB view)

Scope. The toggle is per upload batch, not per file. If it is on, every file in that batch — including every file inside a selected folder — is sent to the decryption service. Mixed batches of encrypted and unencrypted files are not supported; upload them separately.

Default state. Off. It resets to off each time the modal is closed and reopened.

Failure behaviour. If decryption fails, that file's upload fails and the error is shown in the upload progress list / toast. Other files in the batch are unaffected. Nothing partial is indexed.

Progress. The browser's progress bar tracks the upload of bytes to AAEKB only. Time spent in your decryption service happens server-side afterwards and appears as processing time, not upload progress. With the default polling settings, expect at least 5 seconds of additional latency per file even for an instant decryption.

Compatibility: what will and will not work​

aviso

Read this section carefully. It is the source of nearly all integration surprises.

The critical constraint: validation happens before decryption​

AAEKB runs its standard upload security validation against the encrypted bytes and the original filename, before the file is sent to your decryption service. There is no bypass for needs_decryption. This means an encrypted file must still look like a legitimate, allowed file type at the moment of upload.

The validation performs, in order:

  1. Filename checks — no path traversal, no null bytes, no Windows-illegal characters, max 255 characters, no suspicious double extensions.
  2. Extension allowlist — the file must have an extension, and it must be one of the supported types.
  3. Size limit — 4096 MB (4 GB) ceiling.
  4. Content sniffing (libmagic) — the first 2 KB are inspected and compared against the claimed extension. Executable signatures (PE / ELF / Mach-O) are rejected outright.
  5. Type-specific content checks — e.g. images are opened and verified, text files must decode cleanly as text.

Supported file extensions​

Only these extensions are accepted (script types only if your deployment explicitly enables script uploads):

  • Documents: pdf, doc, docx, xls, xlsx, pptx
  • Text and data: txt, md, csv, tsv, json, xml, dita, yaml, yml, html, htm
  • Mail: eml, msg
  • Archives: zip
  • Images: png, jpg, jpeg, gif, webp
  • Audio: mp3, wav, m4a, ogg
  • Video: mp4, mov, avi, wmv, webm
  • Scripts (opt-in only): py, sh, bash, js, php, rb, pl, bat, cmd, ps1

The live list for your deployment is available at GET /upload-capabilities.

Filename and wrapper compatibility​

Uploaded filenameResult
Report.pdf (ciphertext inside)Depends on the ciphertext envelope — see below
Report.pdf.encRejected. .enc is not an allowed extension
Report.enc, Report.drm, Report.aesRejected. Not allowed extensions
Report (no extension)Rejected. "File must have an extension"
Report.pdf.gpgRejected. .gpg is not allowed

Your encryption scheme must preserve the original file extension. A wrapper format that appends .enc, .drm, .p7m or similar will be rejected at the door. If your DRM system produces such filenames, rename to the original extension before upload, or configure the client-side export to retain it.

Ciphertext envelope compatibility​

This is the second gate. The encrypted bytes must survive content sniffing while still claiming the original extension.

Encrypted formatCompatible?Why
Office documents (.docx, .xlsx, .pptx) encrypted with Microsoft Information Protection / OOXML password encryptionYesThese are wrapped in an OLE2 / CDF compound container. AAEKB explicitly accepts OLE2, CDFV2, application/vnd.ms-office and application/octet-stream when the extension is an Office type. This is the best-supported case.
Legacy Office (.doc, .xls) encrypted in placeYesSame OLE2 allowance.
PDF with standard PDF encryption (/Encrypt dictionary, password-protected)YesThe file remains a structurally valid PDF; %PDF- is still the leading signature.
PDF wrapped in an opaque envelope, but with %PDF- present within the first 1024 bytesYesAAEKB searches the first 1 KB for the %PDF- marker and accepts on that basis. A DRM envelope that keeps a PDF preamble/header will pass.
PDF fully encrypted into opaque ciphertext with no %PDF- in the first 1 KBNoSniffing returns application/octet-stream (or something unrelated) for a .pdf extension → rejected with "File content does not match extension" or "File detected as generic binary type."
Plain text formats (.txt, .csv, .json, .md, .xml, .html) encrypted into binary ciphertextNoSniffing does not return a text/* type, so the extension/content check fails.
ZIP archives encrypted as a wholeNoThe outer archive must be a readable ZIP; AAEKB inspects its entries. Additionally, ZIP files containing encrypted members are rejected by the archive validator.
Images, audio, video encrypted into opaque ciphertextNoSame sniffing mismatch.

Rule of thumb: the integration is designed for, and reliably works with, encrypted Office documents and encrypted/DRM-protected PDFs that retain a recognisable header. Formats where encryption destroys all structural markers will be blocked before your service is ever called.

If your DRM system produces fully opaque ciphertext, the supported workaround is to have the exporting system wrap the payload in an OOXML or PDF container that retains the appropriate header bytes, so the file remains recognisable in transit. Alternatively, contact your AAEKB representative — the content-validation layer is configurable at the deployment level and can be adjusted for on-premise installations.

After decryption​

Once the decrypted bytes come back, AAEKB determines the type as follows:

  1. mimetypes.guess_type() on the original filename — so the extension you uploaded with determines the ingestion path in almost all cases.
  2. If that yields nothing, libmagic sniffs the first 2 KB of the decrypted content.

The decrypted content is then routed to the appropriate handler: PDF, Word, Excel, PowerPoint, HTML, XML/DITA, plain text, JSON, CSV/TSV, EML, MSG, audio (transcription), video (transcription), or ZIP (extract and ingest members).

If the type cannot be determined, the file falls through to a generic handler that attempts best-effort text extraction. The upload will still succeed and the file will be stored and downloadable, but it may be indexed with empty text content — meaning it will not be retrievable by semantic search. This is the usual symptom of a decryption service returning something other than the expected format (for example, base64 text instead of raw bytes).

Feature interactions​

FeatureInteraction
Quick Upload toggleCompatible. Decryption runs inline in the request; the user waits for the whole decryption round-trip before the modal completes.
Folder uploadCompatible; the toggle applies to every file in the folder.
Encrypted ZIP as a container of encrypted filesNot supported. Decryption is applied to the uploaded file as a whole, before archive extraction. Members inside a ZIP are not individually decrypted.
URL / connector ingestion (SharePoint, Confluence, web crawl, Google Drive, etc.)Not supported. Decryption applies only to direct file uploads through the Knowledge Base upload modals and the add/file API endpoints.
Chat-attachment uploadsNot exposed in the UI.
Re-sync / re-processing of an existing documentThe stored file is already decrypted, so re-processing works normally and does not call your service again.

Timeouts, limits and sizing​

OperationLimitConfigurable
Submit request to the decryption endpoint300 secondsNo
Each individual poll request60 secondsNo
The follow-up binary fetch after a JSON "completed"120 secondsNo
Total polling windowpoll_interval_seconds × poll_max_attempts — 600 s (10 min) by defaultYes
Maximum upload size4096 MB (4 GB)Deployment-level
Retries on failureNoneNo

Sizing guidance:

  • Decryption is entirely in-memory. Peak memory per concurrent decryption is roughly 2× the file size (ciphertext plus plaintext), on top of normal request overhead. Size your API and worker containers accordingly, or cap practical upload sizes well below the 4 GB ceiling for encrypted uploads.
  • If your service can take longer than 5 minutes to accept a submission, it must use async mode — the 300-second submit timeout is not adjustable.
  • If decryption itself can take longer than 10 minutes, raise poll_max_attempts (or poll_interval_seconds) accordingly.
  • Each in-flight decryption occupies a worker thread for its whole duration, including polling sleep time. High-volume bulk ingestion of encrypted files with long decryption times will consume worker capacity; plan concurrency with that in mind.

Storage, security and audit behaviour​

What gets stored​

AAEKB stores the decrypted file in the Knowledge Base raw file storage, not the ciphertext.

This is because the raw stored file is read back by several parts of the product after ingestion: in-app document preview, download, citation rendering, table and spreadsheet extraction, and any subsequent re-processing or re-indexing of the document. Storing the plaintext keeps all of those working directly, and means the decryption service is called exactly once per document — at upload — rather than on every read. It also removes your decryption service from the critical path for everyday user activity, so an outage there never affects access to documents that are already in the Knowledge Base.

Once ingested, the document is protected by AAEKB's own controls: encryption at rest provided by the underlying object storage, plus AAEKB's project-, team- and role-level access controls governing who can view, search or download it.

Metadata written to each decrypted document​

{
"originally_encrypted": true,
"decryption_service": "Corporate DRM Gateway",
"decrypted_at": 1774000000.123
}
  • decryption_service is the configured Display Name.
  • decrypted_at is a Unix epoch timestamp (seconds, float).
  • originally_encrypted is an informational marker, letting administrators and end users identify which documents arrived encrypted. It does not affect processing: because the stored file is already decrypted, re-processing a document never involves the decryption service again.

Credential handling​

  • auth_token is stored in the AAEKB application database.
  • It is masked whenever the configuration is read back through the admin API or UI: only the first four characters are returned, the remainder replaced with bullet characters. Saving a masked value is detected and ignored, so re-saving the form never corrupts the stored credential.
  • Every change to the configuration is written to the Super Admin audit log as event kb_decryption_service_updated, recording before/after values for all fields except the credential, which is recorded only as auth_token_rotated: true. The token value never appears in audit records or logs.

Access control​

  • Reading and writing the configuration requires Super Admin privileges.
  • The availability status endpoint requires an authenticated session but no special role, and returns only a boolean.
  • Non-admin users can never see the endpoint URL, headers, or credential.

Logging​

AAEKB logs the filename, byte counts, and job identifiers at INFO level during decryption. It does not log file content or credentials. Error messages returned to users include up to the first 500 characters of your service's error response body — avoid returning sensitive material in error bodies.

Error reference and troubleshooting​

Message the user seesHTTPCauseFix
File type '.enc' not allowed. Allowed types include: ...400Non-allowlisted extensionPreserve the original extension on export
File must have an extension400Filename has no extensionAdd the correct extension
File content does not match extension400Ciphertext doesn't sniff as the claimed typeSee Ciphertext envelope compatibility
File detected as generic binary type. Cannot verify file safety.400Opaque ciphertext with a non-Office extensionSee Ciphertext envelope compatibility
Encrypted ZIP files are not allowed400A ZIP with password-protected membersUpload the documents individually, or use an unencrypted archive
File too large (max 4096MB)400Over the size ceilingSplit the document
File was marked as needing decryption, but no external decryption service is currently enabled...400needs_decryption=true sent while the service is disabledEnable the service, or refresh the page so the toggle disappears
Failed to reach the external decryption service: ...502DNS, TLS, connection refused, or timeoutCheck network path, firewall, and TLS trust from both API and worker containers
Decryption service returned HTTP 4xx/5xx: ...502Your service rejected the submitInspect the quoted body; check auth headers
Decryption service returned a non-JSON response without file content.502Async submit returned neither a binary body nor parseable JSONReturn 202 with a JSON job object, or 200 with application/octet-stream
Could not extract job_id from decryption response using path 'job_id'.502poll_job_id_json_path doesn't match your response shapeCorrect the dot path, e.g. data.jobId
Decryption poll returned HTTP 404: ...502Poll endpoint returned 4xx for a not-yet-ready jobReturn 202 while pending
External decryption job failed with status: FAILED502Your service reported a failed statusInvestigate in your service's logs
Decryption job completed but no file stream was returned.502Poll reported completion in JSON but the follow-up GET didn't return binaryReturn the byte stream on completion — see The double-GET behaviour
Decryption service returned empty content.502Zero-length response bodyReturn the actual decrypted bytes
Decryption service timed out after 600s of polling.504Exceeded interval × max_attemptsRaise poll_max_attempts, or speed up decryption
Decryption service config not found. Run the migration first.404Database migration hasn't been appliedRun AAEKB database migrations

Diagnostic tips

  • The toggle doesn't appear → check GET /kb-decryption-service/status; if it returns {"available": false}, the config row is missing or enabled is false. Also allow ~30 s for the browser-side cache to expire.
  • Works on Quick Upload but not on normal upload (or vice versa) → this is almost always a network reachability difference between the API container and the background worker container.
  • Upload succeeds but the document has no searchable content → your service is probably returning something other than raw bytes of the original format (commonly base64 or a JSON wrapper). Verify with curl that the response body starts with the expected magic bytes.

Deployment and networking requirements​

  1. Reachability from two places. Both the AAEKB API containers and the AAEKB background worker containers must be able to resolve and reach decrypt_endpoint and poll_endpoint_template. In Kubernetes deployments, verify that network policies permit egress from both workloads.
  2. TLS trust. AAEKB validates TLS certificates using the standard trusted CA bundle in its container image. A decryption service presenting a certificate from a private/internal CA will fail to connect unless that CA is added to the AAEKB container trust store as part of your deployment. Plan for this — it is a common first-run blocker on intranet deployments. Certificate verification cannot be disabled from the admin UI.
  3. Plain HTTP. http:// endpoints work but are strongly discouraged, as plaintext documents would traverse the network unprotected on the response leg.
  4. Stable hostname. The endpoint is stored as a literal URL. Use a stable internal DNS name or service address rather than a pod IP.
  5. Latency budget. The submit call holds a connection for up to 300 seconds. Ensure any intermediate load balancers, ingress controllers or proxies have idle/read timeouts at least that long, otherwise they will sever the connection before AAEKB gives up.
  6. Database migration. The feature requires the kb_decryption_service_config table, created by a standard AAEKB migration that also seeds a single disabled default row. The feature is inert until a Super Admin enables it.

Rollout and validation checklist​

Before enabling in production

  • Decryption service deployed and reachable from a test pod in the AAEKB namespace (curl from inside the API pod and a worker pod)
  • TLS certificate chain trusted by the AAEKB containers
  • AAEKB database migrations applied; the KB Decryption Service tab loads without a "table not found" error
  • Confirmed with a representative sample that your encrypted files pass upload validation — take three real encrypted documents of each format you intend to ingest and upload them. This validates Ciphertext envelope compatibility, which is the most common failure point

Configuration

  • decrypt_endpoint set, method and content type match your service
  • decrypt_file_field_name matches the form field your service expects (multipart mode)
  • response_mode matches your service's behaviour
  • For async: poll_endpoint_template contains {job_id}; JSON paths verified against a real response captured from your service
  • poll_interval_seconds × poll_max_attempts comfortably exceeds your worst-case decryption time
  • Auth configured and verified with curl using exactly the headers AAEKB will send
  • Display Name set to something meaningful — it is recorded in every decrypted document's metadata

Functional testing

  • Upload an encrypted file with the toggle on → document ingests, content is searchable
  • Upload an unencrypted file with the toggle off → unaffected
  • Upload an unencrypted file with the toggle on → your service should return a clear error; verify the message surfaces sensibly to the user
  • Force a service failure (stop the service) → user sees a 502-class error, no partial document is indexed
  • Test with your largest expected file and confirm timing sits inside the polling budget
  • Test a multi-file batch and a folder upload
  • Verify the decrypted document downloads correctly from the Knowledge Base and that the metadata shows originally_encrypted: true

Operational

  • Credential rotation procedure documented (re-save via the admin UI or PUT API)
  • Monitoring/alerting on the decryption service, including its own error rate and latency
  • Super Admin audit log reviewed to confirm configuration changes are being captured

Known limitations​

  • One service per deployment. There is no per-project, per-team or per-file-type routing to different decryption services.
  • Batch-level toggle only. Encrypted and unencrypted files cannot be mixed in a single upload batch.
  • Direct uploads only. Connector-based ingestion (SharePoint, Confluence, Google Drive, web crawl, and similar) does not invoke decryption.
  • No retries. A single transient network blip fails the upload; the user must retry manually.
  • Static credentials. No OAuth token refresh, HMAC signing, or client-certificate mTLS from AAEKB itself. Use an authenticating proxy for those schemes.
  • No per-user or per-document authorisation context is forwarded to the decryption service. It cannot enforce per-principal DRM policy based on information AAEKB sends.
  • Files are stored decrypted after ingestion, under AAEKB's own access controls and storage encryption. See Storage, security and audit behaviour.
  • No "test connection" button. Validation is done by performing a real upload.
  • Fully opaque ciphertext is blocked by pre-decryption content validation for most formats. See Ciphertext envelope compatibility.
  • Nested decryption is not supported. Members inside a ZIP are not individually decrypted.

FAQ​

Does AAEKB ever see our encryption keys? No. AAEKB sends ciphertext to an endpoint you control and receives plaintext back. It has no knowledge of key material, algorithms, or your DRM policy engine.

Can we run the decryption service entirely on our intranet? Yes — that is the primary use case. The endpoint only needs to be reachable from the AAEKB API and worker containers. For an on-premise AAEKB deployment, nothing needs to leave your network.

Which response mode should we use? Use sync if decryption reliably completes within a few seconds and the service can hold the connection. Use async for anything slower, batched, or queue-backed. async with the poll endpoint returning the byte stream directly on completion is the most robust configuration.

What happens if the decryption service is down? Uploads with the toggle enabled fail with a clear error. Normal uploads are unaffected. Already-ingested documents are unaffected — they are stored decrypted and never need your service again.

Can we disable the feature temporarily? Yes. Toggle Enabled off. The end-user toggle disappears within about 30 seconds, and any upload still claiming needs_decryption=true is rejected with an explanatory message.

Can users tell which documents were originally encrypted? Yes — decrypted documents carry originally_encrypted: true, the decryption service's display name, and a decryption timestamp in their metadata.

Our encrypted files come out of the DRM system named document.pdf.encrypted. Will that work? No. Rename to document.pdf before upload. AAEKB validates the extension before decryption and does not recognise wrapper extensions. See Filename and wrapper compatibility.

Our ciphertext is fully opaque — no recognisable header at all. What are our options? The upload will be rejected before reaching your service. Options: (a) have the exporting system preserve or prepend the original format header, (b) use OOXML/OLE2-based envelope encryption for Office documents, which is explicitly supported, or (c) discuss a deployment-level adjustment to content validation with your AAEKB representative for your on-premise installation.

Can the decryption service return a download URL instead of bytes? No. AAEKB reads the decrypted content from the response body and does not follow a URL returned in JSON.

Does this work with Quick Upload? Yes, though the user waits for the full decryption round-trip before the upload modal completes.

Document version 1.0. Feature reference: EN-3043.