Access Control
A detailed guide to the 6 access control modes
Surflet provides 6 access control modes to govern who can view and interact with a page. Each page can have its own independently configured access policy.
public — Open Access
Anyone with the link can access the page. Suitable for internal announcements and non-sensitive information.
access = surflet.Access.public()
access.public()
Payload structure:
{
"access": {
"mode": "public"
}
}
Note: Public pages still require a valid page URL to access and are not indexed by search engines.
authenticated — Identity Verification
Only users with specified identities can access the page. Users verify their identity via Magic Link or SSO.
access = surflet.Access.authenticated(
emails=["[email protected]", "[email protected]"],
)
access.authenticated({
emails: ['[email protected]', '[email protected]'],
})
Payload structure:
{
"access": {
"mode": "authenticated",
"allowed_identities": [
{"type": "email", "value": "[email protected]"},
{"type": "email", "value": "[email protected]"}
]
}
}
Supported identity types:
| Type | Description | Example |
|---|---|---|
email | Email address | [email protected] |
domain | Email domain (organization-level) | company.com |
group | User group | engineering |
role | Role | admin |
# Organization-level access — anyone in the company can view
access = surflet.Access.authenticated(
identities=[
{"type": "domain", "value": "company.com"},
],
)
# Mixed mode — specific emails + a specific group
access = surflet.Access.authenticated(
identities=[
{"type": "email", "value": "[email protected]"},
{"type": "group", "value": "finance-team"},
],
)
one_time — Single-Use Link
The page can only be viewed once and self-destructs after reading. Ideal for transmitting sensitive information.
access = surflet.Access.one_time(
burn_after=300, # destroy 300 seconds (5 minutes) after viewing
)
access.oneTime({ burnAfter: 300 })
Payload structure:
{
"access": {
"mode": "one_time",
"one_time_options": {
"burn_after_seconds": 300
}
}
}
Behavior:
- The countdown starts when the page is first opened
- After
burn_afterseconds, the page content is permanently deleted - If
burn_afteris not set, the page becomes inaccessible immediately after closing - All access activity is recorded in the audit log
view_limited — View-Count Limit
Limits the total number of times a page can be viewed.
access = surflet.Access.view_limited(max_views=5)
access.viewLimited({ maxViews: 5 })
Payload structure:
{
"access": {
"mode": "view_limited",
"view_limit": 5
}
}
The counter increments by 1 each time the page loads. Once the limit is reached, the page is no longer accessible. Useful for restricting how widely information spreads.
time_limited — Time-Limited Access
The page automatically expires after a specified time.
access = surflet.Access.time_limited(
expires_at="2026-04-01T00:00:00Z",
)
access.timeLimited({ expiresAt: '2026-04-01T00:00:00Z' })
Payload structure:
{
"access": {
"mode": "time_limited",
"expires_at": "2026-04-01T00:00:00Z"
}
}
After expiry, the page automatically transitions to expired status and becomes read-only. You can renew it using the renew API.
# Renew an expired page
client.renew_page("pg_xxx", expires_at="2026-05-01T00:00:00Z")
password — Password Protection
Users must enter a password to access the page.
access = surflet.Access.password(
password_hash="sha256:a1b2c3d4...",
)
access.password({ passwordHash: 'sha256:a1b2c3d4...' })
Payload structure:
{
"access": {
"mode": "password",
"password_hash": "sha256:a1b2c3d4..."
}
}
Security note: Always transmit the hashed password, never the plaintext. Use SHA-256, formatted as
sha256:<hex-encoded-hash>.
Combining with Approval Chains
Access control can be combined with approval chains. For example, an approval page accessible only to designated approvers:
page = client.publish(
title="High-Value Refund Approval",
page_type="approval",
blocks=[...],
actions=[
surflet.Action("act_approve", "Approve", style="primary"),
surflet.Action("act_reject", "Reject", style="danger"),
],
access=surflet.Access.authenticated(
emails=["[email protected]", "[email protected]"],
),
approval_chain={
"mode": "sequential",
"steps": [
{
"step_id": "step_manager",
"assignees": [{"type": "email", "value": "[email protected]"}],
"policy": {"type": "any"},
},
{
"step_id": "step_cfo",
"assignees": [{"type": "email", "value": "[email protected]"}],
"policy": {"type": "any"},
},
],
},
)
Default Behavior
If access is not specified when publishing, the default behavior is:
- The page gets a unique random URL (not guessable)
- Anyone with the URL can access it
- Equivalent to
publicmode