Authentication
The SDK sends a bearer token on every request. It gets that token in one of three ways: a Personal Access Token you pass or put in the environment (recommended), a username and password it exchanges for a session token (interactive use only), or a custom auth strategy object. Separately, data-API keys (dk_…) authenticate callers of your published APIs, not the SDK itself.
Personal Access Tokens
A Personal Access Token (PAT) is an hf_… secret tied to your user. It carries your permissions, optionally narrowed, and is the right credential for scripts, notebooks and CI.
import os
from honeyframeapi import HoneyframeClient
client = HoneyframeClient(
"https://platform.your-domain.com",
token=os.environ["HONEYFRAME_TOKEN"],
)
A token passed this way is used as-is (TokenAuth). It is never refreshed: when it expires or is revoked, calls raise HoneyframeAuthError.
Minting and revoking from code
pat = client.create_pat("nightly-export", expires_days=30)
print(pat["token"]) # the full hf_… secret — shown ONCE, store it now
for t in client.list_pats(): # metadata only, never the secret
print(t["id"], t["name"], t.get("expires_at"))
client.revoke_pat(pat_id) # an id from list_pats()
create_pat(name="", *, expires_days=90):
expires_days | Result |
|---|---|
90 (default) | Expires after 90 days. |
1 … 730 | Expires after that many days. Values above 730 raise ValueError. |
0 | Never expires. Opt in deliberately. |
None | Sends no lifetime; the platform applies its own default (90 days). |
The 90-day default and the 0–730 validation are 0.1.8+; 0.1.7 sends no lifetime unless you pass one.
Narrowing a token
The platform can pin a PAT to one organization or project, make it read-only, and limit it to a list of capability scopes. create_pat does not take those arguments; mint a narrowed token from the UI, or post the documented body through client.request (0.1.8+):
pat = client.request("POST", "/api/auth/tokens", json={
"name": "dashboard-reader",
"expires_days": 30,
"scope_project_id": 7,
"read_only": True,
"scopes": ["dataset.*", "dashboard.view"],
})
The fields and their rules (a scope is a ceiling, never a grant; a narrowed token can only mint equally narrow tokens) are documented in Authentication → Personal Access Tokens.
Rotation
Rotate a PAT by minting the replacement, deploying it wherever the old one is used, then revoking the old one with revoke_pat. Prefer short lifetimes for tokens that live in CI systems.
Username and password
For interactive use, the client can sign in with your email and password. It exchanges them for a session token (8 hours by default) and signs in again automatically when a request returns 401 (LoginAuth).
import getpass
from honeyframeapi import HoneyframeClient
client = HoneyframeClient(
"https://platform.your-domain.com",
username="you@your-domain.com",
password=getpass.getpass(),
)
Prefer a PAT for anything unattended: a password grants everything your account can do, cannot be narrowed, and has to be stored somewhere the job can read it. Never write a password into a script.
Choosing the credential: precedence
The client resolves its credential in this order and uses the first one found:
- an
auth=object (a subclass ofAuthBase); token=;username=together withpassword=;- the
HONEYFRAME_TOKENenvironment variable; - the
HONEYFRAME_USERNAMEandHONEYFRAME_PASSWORDenvironment variables.
If none is found, the constructor raises HoneyframeConfigError before any request is sent.
Environment variables
HoneyframeClient(...) and HoneyframeClient.from_env() read the same variables; keyword arguments win over the environment.
| Variable | Used for |
|---|---|
HONEYFRAME_URL | base_url, when not passed. |
HONEYFRAME_TOKEN | A PAT (or a session token you already hold). |
HONEYFRAME_USERNAME, HONEYFRAME_PASSWORD | Password sign-in, when no token is set. |
HONEYFRAME_PROJECT_ID | The default project (an integer). |
HONEYFRAME_ALLOW_INSECURE_HTTP | Set to 1 to allow a plain-http:// URL to a non-loopback host (see below). |
The organization is not read from the environment. Pass org_id= to the constructor, call client.using(org_id=...), or use the CLI's -o/--org flag.
The older names HUB_API_BASE, HUB_USERNAME, HUB_PASSWORD and HUB_PROJECT_ID are still read as fallbacks, but using one emits a DeprecationWarning. Rename them to the HONEYFRAME_* names.
Keep tokens off the command line
A token passed as a command-line argument is visible to other users in the process list and lands in shell history. Use the environment variable, a secret manager, or for the CLI --token-file PATH / --token - (read from stdin); see CLI.
Organization and project scope
Every request carries X-Org-Id and X-Project-Id headers when the client has an org_id / project_id. Project handles always send their own project id, so one client can drive several projects:
client.using(org_id=42, project_id=7) # changes the client in place, returns it
sales = client.get_project(7)
finance = client.get_project(9) # its calls carry X-Project-Id: 9
A PAT pinned to an organization or project always acts there; the headers cannot point it elsewhere.
Connection security
0.1.8+ (0.1.7 accepts any URL and keeps the httpx defaults):
- HTTPS only.
base_urlmust behttps://. Plainhttp://is accepted only for a loopback host (localhost,127.0.0.1,::1). For a trusted private network, passallow_insecure_http=Trueor setHONEYFRAME_ALLOW_INSECURE_HTTP=1; the client still warns. Anything else raisesHoneyframeConfigErrorbefore a request is sent, because the token (and a password) would travel unencrypted. - Certificate checks.
verify=True(the default) uses the system trust store. Pass a CA bundle path (a file or a directory) for a private certificate authority, or anssl.SSLContext.verify=Falseturns certificate checks off and emits a warning; prefer a CA bundle. - No redirects. The client never follows redirects, so the
Authorizationheader cannot be forwarded to another host. - Encoded paths. Every value the SDK puts into a URL path (dataset names, keys, slugs, ids) is percent-encoded, so a name containing
/,?,#or..cannot re-route a request. - No credentials in exceptions. An exception's
response.requestcarries noAuthorization,CookieorX-API-Keyheader and no body; a failed sign-in keeps no response at all.repr()of the client and the auth objects masks secrets. - Private files. Files the SDK writes for you (exports, downloads, PDFs, notebooks) are created readable by your user only (mode
0600).
client = HoneyframeClient(
"https://platform.internal.example",
token=os.environ["HONEYFRAME_TOKEN"],
verify="/etc/ssl/certs/internal-ca.pem",
)
Data-API keys
A dk_… key lets an outside system call one of your published data APIs or API services without a Honeyframe account. You mint keys with the SDK; the consumer sends the key in the X-API-Key header.
asset = client.get_asset(asset_id) # a published data_api asset
key = asset.create_api_key(name="partner-feed") # an ApiKey object
print(key.key) # the dk_… secret — shown ONCE
create_api_key defaults (0.1.8+; 0.1.7 keys never expire and allow critical):
| Argument | Default | Meaning |
|---|---|---|
expires_days | 90 | 0 = never expires. |
masking_level | "masked" | Values come back masked. |
sensitivity_max | "medium" | The highest column sensitivity tier the key may read. |
rate_limit | 100 | The key's rate limit, stored and enforced by the platform. |
allowed_columns | all | Restrict the key to these columns. |
The sensitivity tiers are low < medium < high < critical. With the default "medium", columns classified high (e-mail, phone, address, date of birth) and critical (national ID, record numbers) come back as [BLOCKED], while a medium column such as a person's name comes back masked. Pass a higher tier only when the consumer needs those columns; an unknown tier raises ValueError.
Manage keys with asset.list_api_keys() and asset.revoke_api_key(key_id). API services mint keys the same way through APIService.create_key; see API services.
Custom auth strategies
auth= accepts any subclass of honeyframeapi.AuthBase. Implement token(http) to return the current bearer string and, optionally, refresh(http) to obtain a new one after a 401 (return True to have the client retry once, False to raise).
from honeyframeapi import AuthBase, HoneyframeClient
class VaultToken(AuthBase):
def __init__(self, read_secret):
self._read = read_secret
def token(self, http):
return self._read()
client = HoneyframeClient("https://platform.your-domain.com",
auth=VaultToken(lambda: "hf_from_your_secret_store"))
Verified against honeyframeapi 0.1.8.