gRPC API reference
FACE is a gRPC API first. The cockpit uses the same public services described here, so any action in the cockpit can also be done by an integration. This section is the reference for that API: how to connect and authenticate, the conventions every service follows, and one page per domain listing each RPC with its messages, fields and errors.
The reference comes from the .proto definitions and the server implementation. Where the two
disagree, the page describes what the server does.
At a glance
| Protobuf package | semantics.v1 |
| API version | 1.0.0 (see Versioning) |
| Transports | Native gRPC over HTTP/2 with TLS; gRPC-web for browsers |
| Authentication | authorization: Bearer <session token> metadata on every call except two |
| Services | 30 public services with 117 RPCs, plus internal RPCs (see Internal services) |
Domains
Connecting
Connect to your FACE instance through the platform edge, over TLS. The edge address is the
host name your instance is published at. This reference uses the placeholder
face.example.internal:443. Both transports reach the same services on that address:
- Native gRPC (HTTP/2 + TLS). Use this for server-to-server integrations, runners and command-line tools. It is the only transport for client-streaming and bidirectional RPCs.
- gRPC-web. Use this for browser clients. The FACE cockpit is a gRPC-web client served from the same origin as the API, and it sends the same bearer token as any other client. gRPC-web carries unary and server-streaming RPCs only.
The cockpit also sends an x-agent-trace-id metadata header on every call. The value is a
random id minted once per cockpit session. ActivityService uses it to scope a subscription to
the runs that session started (see Lineage & activity).
Other clients may send their own id or omit the header.
.proto files instead (see
Getting the protos).Authentication
Every RPC requires a session token in the authorization metadata, except these two:
| RPC | Why it is open |
|---|---|
semantics.v1.IdentityService/Login | It is how a session is obtained. |
semantics.v1.ActivationService/ValidateLicense | It checks a licence before anyone can sign in. |
Nothing else is exempt, including the landing dashboard. Server reflection is not bearer-token authenticated, but it is restricted to the host itself and to verified mutual-TLS mesh peers. It is not an anonymous surface.
Getting a token. Call IdentityService/Login
with one of these methods:
- Email and password, followed by a TOTP code if the account has two-factor enabled.
- An OIDC ID token from the identity provider configured for your deployment. The email in the token must be verified and must be on the deployment’s allowlist.
A successful login returns a signed session token that is valid for 12 hours. Send it on every call:
authorization: Bearer <token>Sessions are checked against the server’s session store on every request. A token that was
revoked by Logout, or by an admin changing the account, stops working within seconds on every
replica.
Machine clients. An org admin can mint a service-account token with
GenerateServiceAccountKey.
It is valid for 365 days and scoped to the admin’s tenant. Store it as a secret.
Tenant scope. The tenant comes from the verified session, never from a request field. A request field that names a tenant is accepted only when it matches the session’s tenant.
Roles
Each session carries one role:
| Role | Summary |
|---|---|
ROLE_ORG_ADMIN | Everything, including user management, service-account keys and org-admin-only decisions. |
ROLE_WRITER | Read and write domain data and run agents. No user management. |
ROLE_READER | Read-only on most services. Some writes, such as setting a compute budget, are refused. |
ROLE_SERVICE_ACCOUNT | Machine credential. Refused on every management surface (below). |
Every service page lists its handler-level role checks. These checks apply across the whole API:
- Service accounts, on unary calls, are refused on every
ConfigServiceRPC exceptListRunnersandListConnections, on all ofActivationService, on everyIdentityServiceRPC exceptListInstances, and on member and role management. On streaming calls they are refused on allConfigServiceandActivationServicestreams. - An unrecognised role grants nothing. The session is refused with
PERMISSION_DENIED. - Subscription tier. On the Lite tier,
ConfigService/CreateDatabricksClusteris refused withPERMISSION_DENIED. - Licence. When the deployment has a licence configured and it is expired or invalid, every
authenticated call fails with
PERMISSION_DENIED(license expired or invalid).
Status codes
FACE uses standard gRPC status codes. The grpc-message text is written for people, and the
cockpit shows it verbatim for refusals. Clients may show it too, but should branch on the code.
| Code | When FACE returns it |
|---|---|
UNAUTHENTICATED | No authorization metadata, or a token that is malformed, expired, revoked or not issued by this deployment. Sign in again. |
PERMISSION_DENIED | The session is valid but not allowed: wrong role, service-account restriction, subscription tier, another tenant’s resource, or an expired licence. |
INVALID_ARGUMENT | A required field is missing or a value is malformed. |
FAILED_PRECONDITION | The request is well formed but the instance cannot serve it yet. Typical causes: no data source is connected, the sovereign model plane is not configured, or a required integration is not set up. Fix the configuration and retry. |
NOT_FOUND | The named record, trace, connection or schedule does not exist for this tenant. |
DEADLINE_EXCEEDED | A runner accepted dispatched work but did not answer in time. The run may still be in progress on the runner, so this does not mean it failed. |
RESOURCE_EXHAUSTED | The caller exceeded the per-caller rate limit, where the operator has enabled one, or a budget or size limit. Back off and retry. |
UNIMPLEMENTED | The RPC is in the protos but not served. Each such RPC is marked on its page. |
UNAVAILABLE | A dependency, such as a runner or the model plane, is temporarily unreachable. Safe to retry with backoff. |
INTERNAL | The server failed. Nothing is claimed to have happened. |
Many RPCs also report an expected refusal in the response body (success: false plus
message) instead of an error. Each RPC’s section says which style it uses.
Unmeasured is not zero. Several responses use explicit presence (proto3 optional) or a
stated reason to separate “nothing measured this” from a measured zero, for example confidence
fields and usage figures. When a field is absent, report it as not measured. Do not display it
as 0.
Streaming
| Kind | RPCs | Transports |
|---|---|---|
| Server streaming | FetchService/RunFetchStream, TwinsService/StreamTwins, TwinsService/UploadAnalyze, HypothesisService/SimulateScenario, SurveillanceService/SubscribeTelemetry, ActivityService/SubscribeAgentActivity, RalphOrchestratorService/ExecuteLoop | Native gRPC and gRPC-web |
| Bidirectional streaming | HypothesisService/InteractiveSimulate, FetchService/Talk, RunnerEnrollmentService/Enroll | Native gRPC only |
Streams are authenticated once, when they open, with the same bearer token and the same rules as
unary calls. A long-lived stream therefore keeps running after its token expires. Re-open it with
a fresh token when you reconnect. Set a deadline (grpc-timeout) on unary calls and reconnect
server streams with backoff.
Versioning
The protos form one module with one version, currently 1.0.0. The build lints the module
with buf and runs a breaking-change check against the previous release. A change that breaks
the wire format or generated code is rejected unless it is explicitly listed as an exception,
with the reason recorded. Additive changes (new fields, RPCs and enum values) arrive without
notice. Clients should ignore unknown fields and treat unknown enum values as unspecified.
Where a field has been retired, its number and name are reserved, so they are never reused
with another meaning. Deprecated RPCs keep working for at least one release and are marked on
their pages.
Getting the protos
The .proto files are provided with the platform. The module is semantics/v1/*.proto under a
buf module root. It imports:
google/protobuf/timestamp.proto(well-known types), andrunink/ui/judgement/v1/judgement.proto, the platform’s shared judgement types, which ship with the FACE protos.
Generate clients with buf generate or protoc for your language. All messages use proto3.
Worked example
This example signs in with grpcurl, then makes an authenticated call. grpcurl reads the
protos from disk rather than using reflection. Run it from the directory that holds the proto
module root (semantics/) and the shared runink/ import tree.
grpcurl \
-import-path . \
-proto semantics/v1/commons_defs.proto \
-d '{"method": "AUTH_METHOD_CREDENTIALS",
"username": "analyst@example.com",
"password": "'"$FACE_PASSWORD"'"}' \
face.example.internal:443 \
semantics.v1.IdentityService/LoginA successful response:
{
"status": "STATUS_SUCCESS",
"token": "eyJhbGciOi...",
"message": "Authentication Successful"
}If the account has two-factor enabled, the response is STATUS_MFA_REQUIRED with an
mfaSession. Send it back within 5 minutes:
grpcurl -import-path . -proto semantics/v1/commons_defs.proto \
-d '{"method": "AUTH_METHOD_MFA_VERIFY",
"mfaSession": "'"$MFA_SESSION"'", "mfaCode": "123456"}' \
face.example.internal:443 semantics.v1.IdentityService/Logingrpcurl prints and accepts JSON field names in lowerCamelCase (mfaSession), and it also
accepts the proto names (mfa_session). Keep passwords and tokens out of your shell history.
The example reads them from environment variables.
Shared types
Judgement
runink.ui.judgement.v1.Judgement is the platform’s verdict on one claim, usually an action
an agent proposed. It appears in fetch results, action cards and the orchestrator stream.
| Field | Type | Description |
|---|---|---|
verdict | Verdict | The verdict. |
reason_code | string | Stable reason code, for example evidence-contradicts-the-claim. |
sentence | string | The sentence a person can act on. It may be partly model-written: render it as text, never as markup. |
basis | Basis | Whether a model was involved. |
subject | string | What was judged, as a person reads it: the action or card title. |
claim | string | The claim judged about the subject. |
accepted | bool | false means the verdict was a dissent and the action was removed from the answer. |
judged_at | google.protobuf.Timestamp | When the judgement ran. Unset means not recorded. It never means “now”. |
ref | string | Where the action sat in the answer, for example decisions[0]. |
evidence_items | int32 | How many evidence items were weighed. 0 with an evidence reason code is a real zero. Otherwise 0 may mean “not reported”. |
round | int32 | 0 for the first answer. 1 and higher for answers the agent loop revised. |
verdict_text | string | The verdict spelled out verbatim. Show it when verdict is VERDICT_UNSPECIFIED. |
JudgementSet
Every verdict from one run, accepted and rejected, with the server’s counts.
| Field | Type | Description |
|---|---|---|
judgements | repeated Judgement | The verdicts. This may be one page of a longer list. |
concurs | int32 | Concurring verdicts. |
dissents | int32 | Dissenting verdicts. |
unable | int32 | Unable-to-judge verdicts. |
Use the counts as sent. Do not recompute them from judgements.
Verdict
| Value | Meaning |
|---|---|
VERDICT_UNSPECIFIED | Not judged, or a verdict this enum does not know. Show verdict_text. |
VERDICT_CONCUR | The evidence supports the claim. |
VERDICT_DISSENT | The evidence contradicts the claim. reason_code names what. |
VERDICT_UNABLE_TO_JUDGE | The evidence is absent, unreadable or does not bear on the claim. Never agreement. |
VERDICT_OUT_OF_SCOPE | The platform has no standing over the subject. Counted in no total. FACE does not produce it. |
Basis
| Value | Meaning |
|---|---|
BASIS_UNSPECIFIED | Not stated. |
BASIS_DETERMINISTIC | Gates and arithmetic only. No model was consulted. |
BASIS_MODEL | A sovereign model answered the qualitative question. |
Internal services
semantics.v1.ModelService (Generate, Embedding, SynthesizeSpeech, SpeakFetchSummary)
is FACE’s internal interface to its own sovereign model plane. It accepts calls only from the
host itself and refuses every other caller with UNAUTHENTICATED, whatever token is presented.
It is not part of the integration surface and is not documented here. Model-backed features are
reached through the domain services that use them.
FetchService/GetDispatchKey is also internal: only a runner serves it, and only to a verified mesh peer.
The same endpoint also serves the platform’s shared cockpit components: estate map, data
sources, billing, profile and member administration. Those services are defined outside the
semantics.v1 package and are not covered by this reference.