Chalk implements OAuth for authentication to the online query interface. Two kinds of credentials can be used to access Chalk resources:

  • Personal Credentials: Full access to all resources on your account. This allows a client to act as you.
  • Service Credentials: Access scoped to a specific project and environment. Used for computers to talk to Chalk. Generated via the web dashboard.

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.


Authenticating an API client

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,
    ],
)


Authenticating CURL

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

Fetching an Access Token

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:

Request

POST
https://api.chalk.ai/v1/oauth/token
Attributes
client_idstring
Your client_id
client_secretstring
Your client_secret
grant_typeclient_credentials
The grant_type field must always be "client_credentials".

Response

Attributes
access_tokenstring
The access_token that you should use in the Authorization header for authenticated requests.
expires_inint
Number of seconds until the access_token expires.
token_typestring
This field will always be "Bearer"

Creating a Service Token in the Dashboard

You 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.

Service Token Creation


Authenticating a Request Using an Access Token

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.


Role-based access control (RBAC)

Use service credentials to limit which features your application can compute and return.

Features

Feature permissions are assigned by tag: you tag your features, then a service token (or role) maps each tag to one of four permissions.

PermissionUsableReturnableDeclassifies
DenyNoNoNo
AllowInternalYesNoNo
AllowYesYesNo
AllowDownstreamYesYesYes
  • Usable — can be computed and used as an input to other features (i.e. appear in the query plan).
  • Returnable — can be returned to the query’s caller.
  • Declassifies — clears the features derived from it, making them returnable too.

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.

Default permission

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):

  • Default 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.
  • Default Deny — only features whose tags are explicitly Allow or AllowDownstream are returnable (an allowlist).

AllowDownstream: the declassification boundary

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 returnable

The override does not apply when the inputs are mixed. Take a feature computed from three inputs that resolve to Allow, AllowDownstream, and Deny:

  • The Deny input taints the result — any Deny in the lineage forces the output to Deny, so it is blocked regardless of the AllowDownstream input.
  • Even setting the 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.