Queries
Access the Chalk API.
Chalk implements OAuth for authentication to the online query interface. Two kinds of credentials can be used to access Chalk resources:
Both personal and service credentials can be used to query Chalk, and potentially to modify your Chalk deployment’s settings. This means that these credentials are sensitive and must be kept secret.
You can create and manage service credentials in the Chalk dashboard or using the Chalk CLI.
When you use the CLI to create credentials, you will be asked to authenticate yourself on Chalk’s web dashboard.
Then, you will receive a client_id and client_secret. Once generated, client_id cannot be changed. However,
client_secret can be rotated if your security practices require this or if you suspect that client_secret has
been compromised.
Once you have generated your client_id and client_secret, you can make authenticated requests to Chalk.
Chalk has published API client libraries for several languages. These libraries handle exchanging a client_id
and client_secret for an access_token which can be used to access Chalk.
from chalk.client import ChalkClient
client = ChalkClient(client_id="...", client_secret="...")
client.query(
input={
User.id: "1",
},
output=[
User.identity.is_voip_phone,
User.fraud_score,
],
)
We recommend using the chalk cli tool to authenticate a curl request. You can use chalk token to acquire an
access_token that is suitable for use as a Bearer token:
curl -H "Authorization: Bearer $(chalk token)" \
https://api.chalk.ai/v1/who-am-i
If you’re implementing a custom API client for a language that Chalk hasn’t published a library for, you may need to
fetch an access_token using the OAuth Client Credentials
grant flow. You can use the token endpoint in Chalk’s API to execute this flow:
client_idstringclient_secretstringgrant_typeclient_credentialsaccess_tokenstringexpires_ininttoken_typestringYou can create a service token in the Chalk dashboard by navigating to the “Service Tokens” tab in the “Settings” page. You can then specify permissions for the token, as well as datasource and feature tags for datasource-based and feature-based RBAC (Role-Based Access Control), respectively.

Use the token obtained from the Client Credentials grant flow in the Authorization: Bearer <ACCESS_TOKEN> header
that your client sends along all authenticated requests. For example:
curl -H "Authorization: Bearer <ACCESS_TOKEN>" https://api.chalk.ai/v1/who-am-i
will return a 200 response and a JSON object containing a short description of the requesting user. This is
convenient for verifying that you are using a valid access_token.
Use service credentials to limit which features your application can compute and return.
Feature permissions are assigned by tag: you tag your features, then a service token (or role) maps each tag to one of four permissions.
| Permission | Usable | Returnable | Declassifies |
|---|---|---|---|
Deny | No | No | No |
AllowInternal | Yes | No | No |
Allow | Yes | Yes | No |
AllowDownstream | Yes | Yes | Yes |
Allow, AllowInternal, and Deny each apply to the single feature they tag:
Allow — the feature can be computed and returned to the caller.Deny — the feature cannot be computed or used at all; any query that needs it, whether as an
output or as an intermediate input, fails.AllowInternal — the feature can be computed and used as an input to other features, but is
never returned to the caller. Use it for sensitive raw values that should stay inside the query.These three grant or deny access to that one tagged feature only — the permission does not flow to
features computed from it. AllowDownstream is the exception: a feature tagged AllowDownstream is
returnable like Allow, but it also acts as a declassification boundary, clearing the features
derived from it. Because that behavior spans the feature graph, it has its
own section below.
When a feature carries multiple tags that map to different permissions, the most restrictive wins:
Deny > AllowInternal > Allow > AllowDownstream
A query is permitted only if every requested output feature resolves to Allow or
AllowDownstream; an AllowInternal or Deny output fails the query.
Every feature whose tags you have not listed takes the token’s default permission, which you may
set to Allow, AllowInternal, or Deny (but not AllowDownstream):
Allow — every feature is returnable unless a tag marks it AllowInternal or Deny
(a blocklist). This is the default, so a token with no feature permissions configured is unrestricted.Deny — only features whose tags are explicitly Allow or AllowDownstream are
returnable (an allowlist).AllowDownstream is the one permission that crosses the feature graph. A feature tagged
AllowDownstream is returnable, and it declassifies features derived from it — but with a strict
rule: a derived feature is cleared (resolves to AllowDownstream, and so becomes returnable) only
when every one of its inputs is AllowDownstream. Declassification therefore propagates along chains
whose entire input set is cleared, and stops the moment a non-cleared input is mixed in.
That same all-inputs rule is what lets AllowDownstream override a Deny: a feature tagged Deny
is still cleared if all of its inputs are AllowDownstream, because the output is then a pure
derivative of already-cleared data.
The canonical use case is an aggregation over sensitive data: the individual records are private,
but a statistic over them is safe to expose. Here the raw transaction amounts are PII
(AllowInternal, never returned), and the per-user average over them is declassified:
from chalk import _
from chalk.features import features, feature, DataFrame
@features
class Transaction:
id: int
user_id: "User.id"
# AllowInternal: usable to compute other features, but never returned on its own.
amount: float = feature(tags=["pii"])
@features
class User:
id: int
transactions: DataFrame[Transaction]
# AllowDownstream: an aggregate over sensitive amounts, safe to expose.
avg_transaction_amount: float = feature(
tags=["cleared"],
expression=_.transactions[_.amount].mean(),
)With a token mapping pii -> AllowInternal and cleared -> AllowDownstream:
$ chalk query --in user.id=1 --out user.avg_transaction_amount # ok: cleared by AllowDownstream
$ chalk query --in transaction.id=1 --out transaction.amount # rejected: AllowInternal, not returnableThe override does not apply when the inputs are mixed. Take a feature computed from three inputs
that resolve to Allow, AllowDownstream, and Deny:
Deny input taints the result — any Deny in the lineage forces the output to Deny, so it is
blocked regardless of the AllowDownstream input.Deny input aside, a mix of Allow and AllowDownstream is not
all-AllowDownstream, so the result is not cleared: it falls back to its own tag (or the default)
and does not declassify features further downstream.In short, AllowDownstream clears a derivative only when the entire lineage feeding it is cleared.
To keep this policy easy to audit, prefer declaring these tags in one central place — see Centrally manage feature-permission tags.