Skip to navigation

File Access and Sharing

GroundX file URLs are public and do not expire by default. On a compatible deployment, account, bucket and document policies can require authentication on every read. The URL stays the same and does not expire.

Availability

File protection requires a deployed file-access gateway, private storage and compatible API and processing services. Setting a policy during creation also requires compatible creation handlers and SDK models. Confirm support on your hosted account or on-prem deployment before using protected data. An API reference entry does not confirm availability. The REST examples below work without new SDK methods; check your SDK version before adapting them.

Inheritance and lifetimes

Documents inherit bucket settings, and buckets inherit account settings. Use the existing resource GET operations to read fileAccess (the saved override), effectiveFileAccess (the combined policy) and fileAccessRevision.

SettingBehavior
Unconfigured accountPublic stable URLs, with no URL expiry
mode: "protected"Stable URLs require the owner’s current authentication on every read and do not expire
urlTtlSecondsMaximum lifetime for new storage-signed share links; does not affect file retention or stable URLs
Omitted policy fieldInherits the corresponding ancestor setting
fileAccess: nullInherits on creation; clears this resource’s override when updating with its current revision

A policy object needs mode, urlTtlSeconds, or both. A bucket or document cannot make content public if its account or bucket requires protection. It also cannot exceed an inherited share-link lifetime limit. A public account or bucket without an explicit lifetime imposes no such limit.

The default share-link lifetime is 300 seconds. Configured values range from 1 through 604800 seconds; storage credentials or provider limits may shorten the actual lifetime. Policies apply to GroundX-owned files. They cannot control previously downloaded copies or externally hosted originals.

Create a bucket or files with a policy

Set the initial policy in the existing create request, without a revision. Omitted or null fileAccess inherits account or bucket settings. This example creates a bucket using an API key stored on your server:

curl --fail-with-body https://api.groundx.ai/api/v1/bucket \
-H "X-API-Key: $GROUNDX_API_KEY" -H 'Content-Type: application/json' \
--data '{"name":"private-documents","fileAccess":{"mode":"protected","urlTtlSeconds":300}}'

Remote ingest accepts fileAccess on each item in documents. Local multipart ingest accepts it in each item’s metadata. Website crawl accepts it on each item in websites. Crawled documents and split files retain their original item’s policy.

{
"documents": [
{"bucketId": 1234, "sourceUrl": "https://files.example/private/reservation-a.pdf", "fileType": "pdf", "fileAccess": {"mode": "protected", "urlTtlSeconds": 120}},
{"bucketId": 1234, "sourceUrl": "https://files.example/private/reservation-b.pdf", "fileType": "pdf", "fileAccess": {"mode": "protected", "urlTtlSeconds": 300}}
]
}

Submit this body to POST /v1/ingest/documents/remote with X-API-Key. Replace the example URLs with the hostedUrl values from the private-upload flow below. Each item keeps its own policy. Poll the returned ingest.processId through GET /v1/ingest/{processId} and inspect individual document status before search. An ingest response does not confirm successful processing.

For a protected file in a public bucket, use private upload before sending the file, then include its protected policy during ingest. Uploading publicly and protecting the document afterward leaves the original upload exposed. GroundX must be able to read remote source URLs; setting a policy does not add support for authenticated third-party URLs.

Change existing settings

Read the resource’s current revision first. Replace or clear only that resource’s override with the revision you read:

{"fileAccess":{"mode":"protected","urlTtlSeconds":300},"fileAccessRevision":0}

Use PUT /v1/customer for your account or PUT /v1/bucket/{bucketId} for a bucket. The account endpoint accepts only its file-access settings. A bucket policy update cannot include a rename. Document updates use the existing request body:

{"documents":[{"documentId":"11111111-1111-4111-8111-111111111111","fileAccess":null,"fileAccessRevision":1}]}

Submit that body to PUT /v1/ingest/documents. A policy update contains exactly one document, with no rename, metadata or callback fields. It does not trigger extraction or indexing.

Omitting fileAccess leaves settings unchanged. Supplying a policy replaces the override; fields omitted from that replacement inherit account or bucket settings. Setting fileAccess to null clears the override and requires the current revision. Read the resource again to confirm the saved settings.

Read a protected stable URL

Use the returned hosted URL on your trusted server, with X-API-Key on every read. Use only configured GroundX file hosts; never forward credentials to arbitrary document URLs or across redirects. An authorized delegated Partner request also uses its normal X-Customer-Key selection. API keys belong in server-side configuration, never query strings, browser code, logs or chat.

import os
import requests
# stable_url is a returned URL on an allowlisted GroundX file host.
response = requests.get(
stable_url,
headers={"X-API-Key": os.environ["GROUNDX_API_KEY"]},
allow_redirects=False,
timeout=30,
)
response.raise_for_status()

Browser applications should use their authenticated application backend to authorize access and either stream the content or issue a short share link. An MCP OAuth token is not a file-host credential. Stable raw URLs support GET/HEAD, byte-range requests and conditional requests. GroundX checks authorization before returning content or confirming that a cached copy is current.

Share a file, page or crop

Call POST /v1/ingest/document/{documentId}/file-link with X-API-Key and a selector for a saved file, page or crop your account owns:

{"asset":{"kind":"page","pageNumber":1,"corrected":false},"expiresInSeconds":120}

The response is { "url": "...", "expiresAt": "..." }. expiresInSeconds can shorten the effective lifetime but cannot exceed it. Use the returned URL unchanged. The recipient needs no GroundX credentials.

Anyone with the link can reuse or forward it until expiry. Revoking an API key or changing a policy does not revoke an existing storage-signed link. A link signed for GET does not permit HEAD or PUT.

AssetSelector
Stored original{"kind":"source"}
Stored raw X-Ray JSON{"kind":"xray"}
Primary extraction output{"kind":"extract"}
Saved page{"kind":"page","pageNumber":1,"corrected":false}
Saved crop by chunk{"kind":"crop","chunkId":"saved-chunk-id"}
Saved crop by position{"kind":"crop","pageNumber":1,"chunkIndex":0}

Use saved page numbers starting at 1. Chunk indices start at 0 within each saved page. Identify a crop by either chunk ID or page and chunk index. Raw URLs, object keys, owner selectors and unrelated fields are rejected.

Upload source files

Use the same upload endpoint for public and protected files, hosted and on-prem. It remains available when file protection is disabled and signs against the deployment’s configured storage. SDKs, widgets and MCP use their configured GroundX API. File bytes go directly to storage. Legacy anonymous upload formats remain compatibility adapters to the same signer; clients do not fall back to a hosted broker.

  1. Call POST /v1/bucket/{bucketId}/file-upload with X-API-Key and {"fileName":"invoice.pdf","fileType":"pdf"} for a public upload. For a protected file, include "fileAccess":{"mode":"protected"} in this request and the later ingest request.
  2. The 201 response supplies uploadId, uploadUrl, hostedUrl, expiresAt, method: "PUT" and headers.
  3. PUT the bytes directly to uploadUrl, unchanged, with exactly the returned required headers. Do not send GroundX keys or customer headers to the storage service.
  4. For a private reservation, before reservation expiry, ingest hostedUrl into the same owned bucket, including the requested initial file policy. Keep the upload reservation and document under the same customer selection.
  5. Poll ingest status and read the document’s confirmed policy.

The upload endpoint validates the requested file policy before returning any storage grant. A non-null policy returns 403 when protection is disabled, before bytes leave the client. Omitted or null policy inherits.

PUT must finish before the upload grant expires. Public uploads have no private reservation, even when the file-protection feature is enabled. Expiry of the PUT grant does not expire the uploaded source or prevent later ingest. The reply’s effectiveFileAccess reports the policy used at issuance. Only protected uploads need an ingest deadline; older replies without this field should retain protected-upload checks.

The private upload reservation is tied to its bucket and owner. The upload link lasts at most 900 seconds, regardless of the read-share lifetime. Storage credentials may shorten it. If it expires, request a new reservation and upload again. Do not use public upload as a fallback.

An MCP host must be able to read the local file and perform HTTP PUT to complete this upload. Private upload also supports protected files in public buckets.

For protected documents, returned xrayUrl uses ?view=links. Authenticate as the owner to get fresh links to the document’s stored source, pages and crops. GroundX updates the links when serving this JSON; it does not store a new copy. This view requires GET, rejects Range and returns fresh JSON without raw-file ETags or 304 responses. Large responses stream through the file gateway, outside the buffered API.

The X-Ray URL without ?view=links returns the stored raw JSON. An xray share link shares only that JSON, without granting access to the files it references. Preserve the raw file when exact bytes matter, such as for fixtures or evidence. The existing getXray API remains available within its response-size limits.

For protected owned assets, search sourceUrl, multimodalUrl, pageImages and pages[].imageUrl contain storage-signed links that expire within the effective share-link lifetime. Ordinary public stable links remain unchanged. Retain documentId, chunkId and saved page numbers separately. When a link expires, call documents.fileLink (POST /v1/ingest/document/{documentId}/file-link) with the source, crop or page selector from those identifiers, or re-run an authorized search. For document review, fetch xrayUrl again. A failed link is not a reason to make the resource public or expose a key in the browser.

Errors and deployment prerequisites

ResultAction
400Fix the request body, missing update revision, selector, lifetime or inherited policy restriction
401 / 403 / 404Check environment, owner, credentials and resource visibility; do not retry anonymously
409Re-read settings for a stale revision, or resolve the reported ownership conflict or missing file protection
404 when requesting a share linkFile access is disabled, or the owned resource or asset is absent; confirm deployment support and resource visibility. Upload issuance remains available without protection
403 when creating or updating with a policyFile access is disabled, or the request is forbidden; confirm deployment support and permissions
503 with Retry-AfterFile access is enabled but policy checks, ownership checks or storage are temporarily unavailable; wait for the stated delay and read the saved settings
Storage rejects an expired linkRequest a new link through the authenticated API

Policy updates are saved in one transaction. If it fails before commit, the previous settings and revision remain. After a timeout or lost response, read the settings before retrying because the update may have succeeded. Reusing an old revision then returns 409. Updates do not remain pending or recover in the background.

On-prem deployments use the same API requests and responses with local API and file endpoints. They require:

  • Compatible API, processor and file-gateway images.
  • Private storage for reads and writes, with reachable storage-signed URLs.
  • TLS certificates and trust settings.
  • Shared policy, cache and database configuration.
  • Verified protection for existing files before applying a policy to them.

Each supported storage backend needs acceptance tests against its exact version. S3 compatibility alone does not certify MinIO, AIStor or S4. Hosted support does not confirm on-prem support. Local deployments may not provide hosted OAuth discovery. See GroundX On-Prem on AWS and Debugging GroundX On-Prem.