Operations and trust — reference

The exhaustive catalog for the operational surface: webhook configuration and delivery, the event envelope, the usage report, the activity log, and teardown. For the conceptual model see explanation.md; for runnable recipes see how-to.md. For the raw endpoint/parameter listing, see the generated API reference (the OpenAPI/Scalar spec) — this page documents behavior, fields, limits, and honest limitations, not the wire schema.


Webhooks

Configuration object

A webhook registration is per environment (live and test tenants are registered separately). Fields, as returned by the developer-portal webhook endpoints:

FieldTypeNotes
idstringThe webhook configuration id.
urlstringFull HTTPS endpoint. Must use https://; plain HTTP is rejected.
domainstringHostname extracted from url, stored for the delivery-time check.
eventsstring[]Subscribed event types. Must be non-empty.
statusstringACTIVE or DISABLED.
disabledReasonstring | nullconsecutive_failures, manual, or ssrf_blocked when disabled.
consecutiveFailuresintRunning count of consecutive delivery failures; reset to 0 on any success or on re-enable.
apiVersionstringPayload format version. Defaults to 2024-01.
tenantIdstringThe tenant (live or test) the registration belongs to.
createdAtnumberCreation timestamp (epoch millis).
secretstringReturned only in the create response. 64-char hex (32 random bytes). Never returned by GET/list.

Operations

OperationMethod / pathNotes
RegisterPOST /developer/webhooks201; returns the secret once.
Get oneGET /developer/webhooks/{id}200; secret omitted.
ListGET /developer/webhooks?tenantId=...200; tenantId query param required; secret omitted.
UpdatePUT /developer/webhooks/{id}Change url, events, apiVersion, or status.
DeleteDELETE /developer/webhooks/{id}204. There is no in-place secret rotation — delete and re-create.
List deliveriesGET /developer/webhooks/{id}/deliveries200; delivery records, payload bodies omitted.
Retry a deliveryPOST /developer/webhooks/{id}/deliveries/{deliveryId}/retryRe-queues a FAILED delivery.

Registration validation rules

Enforced at POST (and re-run on PUT when the URL changes):

  • url is required and must start with https://.
  • events must be present and non-empty.
  • tenantId is required and must be one of your own tenants (live or test) — otherwise 403.
  • The URL hostname must resolve, and every resolved IP must be publicly routable. A hostname resolving to any private, loopback, link-local, any-local, multicast, CGNAT, or IPv6 unique-local-address range is rejected with 400. This includes the cloud metadata address.
  • The hostname's domain must be verified for your account, or the registration is rejected with 403.

The delivery-time SSRF gate

The registration-time DNS check is defense-in-depth and fast feedback; the real security boundary is re-validation at delivery. Immediately before each POST, the destination hostname is re-resolved and every resolved IP must pass the same public-routability predicate. The predicate rejects:

  • Loopback (127.0.0.0/8, ::1)
  • IPv4 private ranges (RFC 1918 site-local)
  • Link-local (169.254.0.0/16, fe80::/10) — including the cloud metadata IP
  • Any-local and the 0.0.0.0/8 "this network" block
  • Multicast
  • CGNAT 100.64.0.0/10 (RFC 6598)
  • IPv6 unique-local fc00::/7 (RFC 4193)
  • IPv4-mapped/compatible IPv6 forms are unwrapped to their IPv4 address first, so a private IPv4 cannot hide inside an IPv6 wrapper.

If delivery-time resolution returns a non-public IP, the delivery is failed and the webhook is auto-disabled with disabledReason = ssrf_blocked — a DNS-rebinding attempt takes the registration offline rather than merely dropping one delivery. A null or unresolvable address fails closed (treated as non-public).

The event envelope

The body delivered to your endpoint:

KeyTypeNotes
idstringUnique delivery id; also sent as the X-Vectros-Delivery header.
versionstringEnvelope version, currently 2024-01.
typestringThe event type (see below).
creatednumberUnix seconds at envelope build time.
tenantIdstringThe tenant the event belongs to.
livemodebooleantrue for the live tenant, false for test.
dataobjectEvent-type-specific fields.

data for document.* events: id (document id), status, indexMode, and optionally userId, orgId, clientId, folderId when present. The document title is intentionally excluded — it is the filename, a top-level (non-typed) value that the field-masking machinery does not cover, so it is never egressed in an envelope. Retrieve the title via GET /v1/documents/{id}, where reveal-scope and tenant/scope enforcement apply.

data for record.* events: id (record id), typeName, indexStatus, and optionally userId, orgId, clientId when present.

The webhook data field names match the public REST API surface exactly — typeName and userId are the same keys the REST request/response uses, so a consumer can share models across both surfaces without remapping.

Event types:

EventFires when
document.indexedA document finishes indexing successfully.
document.failedA document fails indexing.
record.indexedA record finishes indexing successfully.
record.failedA record fails indexing.

Adding new fields to data is a non-breaking, no-version-bump change; removing or renaming a field would require a version bump. Pin apiVersion on the registration if you need the envelope structure frozen.

Delivery headers

HeaderValue
Content-Typeapplication/json
X-Vectros-DeliveryThe delivery id.
X-Vectros-TimestampUnix seconds at signing time.
X-Vectros-Signaturesha256=<hex> — HMAC-SHA256 of "<timestamp>.<body>" keyed by the hex-decoded secret.

Signing and verification contract

  • The signature is computed over the literal string <timestamp>.<body>, where <body> is the exact bytes of the JSON payload and <timestamp> is the value in the X-Vectros-Timestamp header.
  • The secret is hex; decode it to its 32 raw bytes before using it as the HMAC key.
  • The signature is recomputed fresh on every attempt (including retries) so the timestamp is always current. Receivers should reject a delivery whose timestamp is more than 300 seconds from now — this is the replay window.
  • Use a constant-time comparison when checking the signature.

Delivery, retry, and auto-disable

  • Delivery is at-least-once. Your receiver must be idempotent — dedupe on the delivery id.
  • The HTTP POST uses a 5-second connect timeout and a 30-second response timeout. A 2xx response marks the delivery DELIVERED; anything else (non-2xx, timeout, connection error) is a failure.
  • Retry backoff after the first attempt fails: 30s → 5m → 30m → 2h → 8h. After the fifth retry delay is exhausted the delivery is marked FAILED and not retried automatically (you can re-drive it manually).
  • Each consecutive failure increments the webhook's consecutiveFailures; a success resets it to 0. At 10 consecutive failures the webhook is auto-disabled with disabledReason = consecutive_failures. Re-enable via PUT {"status":"ACTIVE"}, which resets the counter.
  • Delivery records have a 7-day TTL while unresolved; the TTL is cleared once a delivery reaches DELIVERED so it remains visible in the portal. A tenant teardown deletes delivery rows explicitly (they may carry event identifiers) rather than waiting on the TTL.

Delivery record fields (history view)

GET /developer/webhooks/{id}/deliveries returns, per delivery: id, webhookId, eventType, sourceId, sourceType, status (PENDING / DELIVERED / FAILED), attempts, nextRetryAt, createdAt. The limit query parameter caps the count (default 50, max 200). The envelope payload is never returned by this endpoint — it may carry identifiers.

Notes & limits — webhooks

  • Events are limited to the four indexing events above. There is no webhook for synchronous CRUD operations, deletes, search, inference, identity, or billing.
  • Registration is per environment; there is no account-wide registration spanning live and test.
  • No in-place secret rotation — delete and re-create to roll the secret.
  • No payload customization, header injection, or per-event endpoint routing — one registration receives all of its subscribed event types at one URL.
  • Manual retry only applies to deliveries in FAILED status.

Usage and billing

getUsage — the report

client.auth.getUsage() (GET /v1/usage). Not enveloped — returns the report object directly. Requires billing:r on a scoped token (a partner root key always passes). With no arguments returns the current calendar period; { year, month } selects a specific period.

FieldTypeNotes
periodstringYYYY-MM.
credits.usednumberCredits consumed this period, rounded down to whole credits.
credits.usedMillinumberExact consumption in milli-credits (1 credit = 1000 milli-credits; use this for reconciliation).
credits.limitnumber?The period allowance, when applicable.
search.queries.text.countnumberTEXT searches this period.
search.queries.semantic.countnumberSEMANTIC searches this period.
search.queries.hybrid.countnumberHYBRID searches this period.
documents.ingest.text.countnumber?Inline-text document ingests.
documents.ingest.file.countnumber?File-upload document ingests.
records.writes.countnumber?Record writes this period.
records.writes.lookupFieldCountnumber?Lookup-field writes (one per lookup field per write).
inference.balanceCentsnumberPre-paid inference balance in cents. Never negative — the deduct path floors at 0.
inference.endpoints.chat.callsnumberChat calls this period.
inference.endpoints.rag.callsnumberRAG calls this period.
inference.endpoints.ask.callsnumberDocument-ask calls this period.
tenants.liveobjectSame shape (credits / search / inference) scoped to the live tenant.
tenants.testobjectSame shape scoped to the test tenant.

The two-axis model

  • Monthly credit allowance — covers data-plane work (record writes, document ingests, searches), resets each calendar month, reported by credits.
  • Pre-paid inference balance — covers chat / RAG / document-ask, denominated in cents, drawn down per inference call and topped up out of band, reported by inference.balanceCents.

Metering semantics

  • Counts are tracked at the partner level and broken down per tenant; the partner total reconciles to tenants.live + tenants.test.
  • Reads do not draw down the credit allowance — only writes and searches count.
  • Counters tick as operations dispatch (e.g. a document ingest ticks at dispatch, before indexing completes; a chat call ticks after the stream finalizes), so the report is near-real-time and eventually consistent with in-flight settlement.
  • The inference endpoint section always carries all three keys (chat, rag, ask) even at zero — defaulting is per-endpoint.
  • The all-three inference keys are present on both the partner-level and per-tenant sections.

Notes & limits — usage

  • The report is read-only and observability-oriented; it is not an invoice or a line-item transaction export.
  • Rounding: credits.used rounds the partner total, which can differ from the sum of the already-rounded per-tenant values by up to one cent — reconcile on usedMilli, not used.
  • Pricing rates, plan allowances, and overage policy are not part of this surface; this documentation does not state numeric prices.

Activity log

client.auth.getAdminLogs(params) (GET /v1/admin/logs). Requires logs:r on a scoped token (a partner root key always passes). The tenant is derived from the credential — there is no request channel to query another tenant.

Parameters

ParameterTypeNotes
startTimeISO-8601 stringStart of the query window.
endTimeISO-8601 string?End of the window. An endTime earlier than startTime returns 400.
limitint?Caps returned entries.
errorsOnlyboolean?When true, only entries with status >= 400.
resourcestring?Allow-list-validated resource filter (e.g. search).
methodstring?Allow-list-validated HTTP method filter (e.g. GET).
keyIdstring?Filter to entries authored by one key.

Response

FieldTypeNotes
entriesarrayLog entries, newest first.
truncatedbooleanTrue if the window held more than limit entries.
queryDurationMsnumberBackend query latency.
tenantIdstringThe tenant the query ran against (credential-derived).

Each entry: timestamp (ISO-8601), method, resource, status, and optionally keyId, durationMs, path.

Notes & limits — activity log

  • The resource and method filters are allow-list validated at the boundary; an out-of-allow-list value is rejected, not silently dropped.
  • There is a short ingestion lag (seconds) between a request completing and its entry becoming queryable.
  • This is an operational API call log, not the compliance audit/version history (which is a separate, retained data-layer mechanism — see compliance.md).

Teardown and erasure

Per-customer / per-context hard-delete — implemented

  • Context delete runs an owner-filtered cascade that removes the records, documents, folders, and schemas under a context (the isolation boundary). It is the mechanism for removing one customer's or one application's footprint.
  • Tenant teardown decommissions an entire live or test tenant, cascading across its contexts and tenant-level config (including webhook registrations and delivery rows, which are deleted explicitly because they may carry identifiers).
  • These operations are irreversible and are control-plane actions.

End-subject right-to-erasure — RESERVED (not implemented)

  • POST /v1/erasure-requests and GET /v1/erasure-requests/{id} exist as a frozen contract stub. The request/response shapes are stable so SDK integrations will not break when the engine ships, but the endpoint returns 501 {"error":"not_implemented"} today — there is no erasure engine behind it yet.
  • The endpoint requires the root partner API key; a scoped credential (ssk_* / st_*) is rejected with a uniform 403 before the stub runs.
  • This is distinct from context/tenant hard-delete. Per-customer deletion works today; per-individual ("erase everything about this one subject everywhere") does not.

Notes & limits — teardown

  • Right-to-erasure is reserved, not turnkey.
  • Read-access logging / accounting-of-disclosures is a separate reserved surface (its read-side endpoint likewise returns 501 today) — see compliance.md.
  • Data-retention periods are platform constants today; they are not configurable per controller.

Where to go next

  • explanation.md — the concepts behind everything cataloged here.
  • how-to.md — runnable recipes for webhooks, usage, and the activity log.
  • compliance.md — the trust posture, the three sensitive-data mechanisms, retention, and the full reserved list.