Security & Access API
Every Security & Access action (14) and data model (6): endpoints, scopes, input, preconditions, events and examples.
Roles, and what each role can see and do.
Module code security_access · 14 actions · 6 data models · 15 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: security_access (everything), security_access:read (read only) or security_access:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
access_grants | access_grant | /api/v1/access_grants | |||
blueprint_installs | blueprint_install | /api/v1/blueprint_installs | |||
role_module_capabilities | role_module_capability | /api/v1/role_module_capabilities | |||
roles | role | /api/v1/roles | security_access.create_role | ||
workspace_members | workspace_member | /api/v1/workspace_members | |||
workspace_modules | workspace_module | /api/v1/workspace_modules |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
security_access.apply_blueprint | security_access:manage | Install a blueprint |
security_access.create_role | security_access:manage | Create role |
security_access.delete_role | security_access:delete | Delete role |
security_access.grant | security_access:manage | Grant access to one person |
security_access.open_shop | security_access:manage | Open the shop |
security_access.remove_blueprint_samples | security_access:manage | Remove sample data |
security_access.revoke | security_access:manage | Revoke a personal grant |
security_access.set_capability | security_access:manage | Grant or revoke a capability |
security_access.set_masked_fields | security_access:manage | Hide fields from one person |
security_access.set_member_role | security_access:manage | Change a member’s role |
security_access.set_reports_to | security_access:manage | Set who a member reports to |
security_access.set_role_channels | security_access:manage | Limit a role to sales channels |
security_access.toggle_feature | security_access:manage | Switch part of a module on or off |
security_access.toggle_module | security_access:manage | Switch a module on or off |
security_access.apply_blueprint
Install a blueprint — Sets a workspace up for one kind of business — modules with their dependencies, words, settings, roles, services, pipeline, tax, shipping, fields, tags, dashboards, agents and, if asked, sample data — or re-applies it.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_modules/actions/apply_blueprint- Canonical address:
POST /api/v1/actions/security_access.apply_blueprint
Input
| Field | Type | Required | Notes |
|---|---|---|---|
code | string | yes | Blueprint · max 41 |
choices | object / array | Onboarding choices · Answers, module switches, edited setup areas, agents, workflows, sample data. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_blueprint | The blueprint, and every module it names, must exist in this build. |
blueprint_downgrade | A newer version than this build’s must not be overwritten with an older one. |
invalid_choices | The onboarding choices must be an object of lists and answers. |
blueprint_step_failed | Every configuration step must pass its own action’s rules, or nothing is installed. |
Events it publishes: security_access.blueprint_applied, security_access.module_enabled
What it writes: workspace_module (upsert) — Switches the pack’s modules on, and sets their sidebar order once; role (insert) — Creates the pack’s roles; role_module_capability (insert) — Grants those roles their starting capabilities; blueprint_install (upsert) — Records the version applied and the choices it was applied with
curl -X POST https://app.getera.io/api/v1/workspace_modules/actions/apply_blueprint \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"code":"Blueprint"}'{
"ok": true,
"result": "…",
"events": [
"security_access.blueprint_applied",
"security_access.module_enabled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceModules.applyBlueprint({
code: "Blueprint"
})era workspace_modules apply_blueprint \
--code "Blueprint"{
"name": "era__security_access__apply_blueprint",
"arguments": {
"code": "Blueprint"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_blueprint, blueprint_downgrade, invalid_choices, blueprint_step_failed · 429 slow down
security_access.create_role
Create role — Defines a role for this workspace, starting with no access to anything but the person’s own Era Station rows.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/roles/actions/create_role- REST alias:
POST /api/v1/roles - Canonical address:
POST /api/v1/actions/security_access.create_role
Input
| Field | Type | Required | Notes |
|---|---|---|---|
code | string | yes | Code · max 40 |
label | string | yes | Name · max 80 |
description | string | Description · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
code_format | The code must start with a letter and contain only lowercase letters, numbers and underscores. |
code_taken | A role with that code already exists in this workspace. |
Events it publishes: security_access.role_created
What it writes: role (insert) — Creates the role; role_module_capability (insert) — Grants the personal modules (each person’s own Era Station rows), by trigger (sql/818)
curl -X POST https://app.getera.io/api/v1/roles/actions/create_role \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"code":"Code","label":"Name"}'{
"ok": true,
"result": "…",
"events": [
"security_access.role_created"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.roles.create({
code: "Code",
label: "Name"
})era roles create \
--code "Code" \
--label "Name"{
"name": "era__security_access__create_role",
"arguments": {
"code": "Code",
"label": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 code_format, code_taken · 429 slow down
security_access.delete_role
Delete role — Removes a role nobody holds.
Needs security_access:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/roles/actions/delete_role- Canonical address:
POST /api/v1/actions/security_access.delete_role
Input
| Field | Type | Required | Notes |
|---|---|---|---|
role_id | uuid | yes | Role |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
system_role_protected | The six system roles cannot be deleted. |
role_in_use | Members still hold this role. Move them first. |
Events it publishes: security_access.role_deleted
What it writes: role (delete) — Deletes the role and its capability rows
curl -X POST https://app.getera.io/api/v1/roles/actions/delete_role \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"role_id":"ROLE_ID"}'{
"ok": true,
"result": "…",
"events": [
"security_access.role_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.roles.deleteRole({
role_id: "ROLE_ID"
})era roles delete_role \
--role-id "ROLE_ID"{
"name": "era__security_access__delete_role",
"arguments": {
"role_id": "ROLE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 system_role_protected, role_in_use · 429 slow down
security_access.grant
Grant access to one person — Gives one person access to one thing, optionally narrowed or with fields hidden.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/access_grants/actions/grant- Canonical address:
POST /api/v1/actions/security_access.grant
Input
| Field | Type | Required | Notes |
|---|---|---|---|
user_id | uuid | yes | Member |
subject_type | string (enum) | yes | Kind · Allowed values: module, report, view |
subject_key | string | yes | Which one · max 80 |
capability | string | yes | Capability · max 24 |
row_scope | string (enum) | Rows they see · Allowed values: all, own_records, own_and_reports | |
masked_fields | object / array | Measures to hide · Names of measures this grant must not show. Only meaningful for a report. | |
channel_ids | object / array | Allowed sales channels · Retail only: null = all, empty array = none. Narrows the role scope. | |
expires_at | date-time | Expires |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_scope_invalid | Channel scope must contain channels from this workspace and target retail. |
not_a_member | They are not a member of this workspace. |
subject_not_found | A report grant must name a report the registry declares. |
measure_not_maskable | A report may only be asked to hide a measure it actually shows. |
Events it publishes: security_access.access_granted
What it writes: access_grant (upsert) — Records the grant
curl -X POST https://app.getera.io/api/v1/access_grants/actions/grant \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"user_id":"USER_ID","subject_type":"module","subject_key":"Which one","capability":"Capability"}'{
"ok": true,
"result": "…",
"events": [
"security_access.access_granted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.accessGrants.grant({
user_id: "USER_ID",
subject_type: "module",
subject_key: "Which one",
capability: "Capability"
})era access_grants grant \
--user-id "USER_ID" \
--subject-type "module" \
--subject-key "Which one" \
--capability "Capability"{
"name": "era__security_access__grant",
"arguments": {
"user_id": "USER_ID",
"subject_type": "module",
"subject_key": "Which one",
"capability": "Capability"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_scope_invalid, not_a_member, subject_not_found, measure_not_maskable · 429 slow down
security_access.open_shop
Open the shop — One call for a workspace with no shop: switches the shop modules on (retail and what it needs) and opens a sales channel named after the workspace, in TRY. Era calls it after "Mağazan yok — açayım mı?" is answered yes.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_modules/actions/open_shop- Canonical address:
POST /api/v1/actions/security_access.open_shop
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Name · Empty = the workspace name. · max 250 | |
currencyCode | string | Currency · ISO-4217. Empty = TRY. · max 3 | |
defaultCountry | string | Default country · Empty = TR. · max 2 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
module_limit_reached | The workspace must have room for the shop modules on its plan. |
invalid_currency | The currency must be an ISO-4217 code. |
Events it publishes: security_access.module_enabled, security_access.shop_opened
What it writes: workspace_module (upsert) — Switches retail and its dependencies on; retail’s opening code then opens the shop channel, as a seed would
curl -X POST https://app.getera.io/api/v1/workspace_modules/actions/open_shop \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"security_access.module_enabled",
"security_access.shop_opened"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceModules.openShop({
name: "Name"
})era workspace_modules open_shop \
--name "Name"{
"name": "era__security_access__open_shop",
"arguments": {
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 module_limit_reached, invalid_currency · 429 slow down
security_access.remove_blueprint_samples
Remove sample data — Removes only records written by the blueprint installer, through each record’s module action. Safe to run again.
Needs security_access:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/blueprint_installs/actions/remove_blueprint_samples- Canonical address:
POST /api/v1/actions/security_access.remove_blueprint_samples
Input
This action takes no input.
What it writes: blueprint_install (update) — Marks successfully handled sample records removed
curl -X POST https://app.getera.io/api/v1/blueprint_installs/actions/remove_blueprint_samples \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.blueprintInstalls.removeBlueprintSamples()era blueprint_installs remove_blueprint_samples{
"name": "era__security_access__remove_blueprint_samples",
"arguments": {}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down
security_access.revoke
Revoke a personal grant — Takes back access given to one person directly.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/access_grants/actions/revoke- Canonical address:
POST /api/v1/actions/security_access.revoke
Input
| Field | Type | Required | Notes |
|---|---|---|---|
grant_id | uuid | yes | Grant |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
grant_not_found | That grant no longer exists. |
Events it publishes: security_access.access_revoked
What it writes: access_grant (delete) — Removes the grant
curl -X POST https://app.getera.io/api/v1/access_grants/actions/revoke \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"grant_id":"GRANT_ID"}'{
"ok": true,
"result": "…",
"events": [
"security_access.access_revoked"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.accessGrants.revoke({
grant_id: "GRANT_ID"
})era access_grants revoke \
--grant-id "GRANT_ID"{
"name": "era__security_access__revoke",
"arguments": {
"grant_id": "GRANT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 grant_not_found · 429 slow down
security_access.set_capability
Grant or revoke a capability — Decides what one role may do in one module.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/role_module_capabilities/actions/set_capability- Canonical address:
POST /api/v1/actions/security_access.set_capability
Input
| Field | Type | Required | Notes |
|---|---|---|---|
role_id | uuid | yes | Role · id of role |
module_code | string | yes | Module · max 41 |
capability | string | yes | Capability · max 24 |
granted | boolean | yes | Granted |
own_records_scope | string (enum) | Reaches · Only meaningful for “own records only”. · Allowed values: self_only, self_and_reports |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
system_role_protected | The owner role always reaches everything. |
module_not_enabled | That module is not switched on in this workspace. |
capability_not_declared | That module does not offer that capability. |
last_owner_protected | Someone must keep access to roles. |
Events it publishes: security_access.capability_granted, security_access.capability_revoked
What it writes: role_module_capability (upsert) — Adds or removes the matrix row
curl -X POST https://app.getera.io/api/v1/role_module_capabilities/actions/set_capability \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"role_id":"ROLE_ID","module_code":"Module","capability":"Capability","granted":true}'{
"ok": true,
"result": "…",
"events": [
"security_access.capability_granted",
"security_access.capability_revoked"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.roleModuleCapabilities.setCapability({
role_id: "ROLE_ID",
module_code: "Module",
capability: "Capability",
granted: true
})era role_module_capabilities set_capability \
--role-id "ROLE_ID" \
--module-code "Module" \
--capability "Capability" \
--granted true{
"name": "era__security_access__set_capability",
"arguments": {
"role_id": "ROLE_ID",
"module_code": "Module",
"capability": "Capability",
"granted": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 system_role_protected, module_not_enabled, capability_not_declared, last_owner_protected · 429 slow down
security_access.set_masked_fields
Hide fields from one person — Keeps the rows visible and the named fields hidden.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/access_grants/actions/set_masked_fields- Canonical address:
POST /api/v1/actions/security_access.set_masked_fields
Input
| Field | Type | Required | Notes |
|---|---|---|---|
grant_id | uuid | yes | Grant |
fields | string | Fields to hide · Comma separated. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
grant_not_found | That grant no longer exists. |
measure_not_maskable | A report grant may only hide a measure that report actually shows. |
Events it publishes: security_access.access_granted
What it writes: access_grant (update) — Updates the hidden field list
curl -X POST https://app.getera.io/api/v1/access_grants/actions/set_masked_fields \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"grant_id":"GRANT_ID"}'{
"ok": true,
"result": "…",
"events": [
"security_access.access_granted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.accessGrants.setMaskedFields({
grant_id: "GRANT_ID"
})era access_grants set_masked_fields \
--grant-id "GRANT_ID"{
"name": "era__security_access__set_masked_fields",
"arguments": {
"grant_id": "GRANT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 grant_not_found, measure_not_maskable · 429 slow down
security_access.set_member_role
Change a member’s role — Moves someone to a different role in this workspace.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_members/actions/set_member_role- Canonical address:
POST /api/v1/actions/security_access.set_member_role
Input
| Field | Type | Required | Notes |
|---|---|---|---|
user_id | uuid | yes | Member |
role_id | uuid | yes | Role · id of role |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
cannot_change_own_role | You cannot change your own role. |
role_not_in_workspace | That role belongs to a different workspace. |
not_a_member | They are not a member of this workspace. |
Events it publishes: security_access.role_changed
What it writes: workspace_member (update) — Points the member at the new role
curl -X POST https://app.getera.io/api/v1/workspace_members/actions/set_member_role \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"user_id":"USER_ID","role_id":"ROLE_ID"}'{
"ok": true,
"result": "…",
"events": [
"security_access.role_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceMembers.setMemberRole({
user_id: "USER_ID",
role_id: "ROLE_ID"
})era workspace_members set_member_role \
--user-id "USER_ID" \
--role-id "ROLE_ID"{
"name": "era__security_access__set_member_role",
"arguments": {
"user_id": "USER_ID",
"role_id": "ROLE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 cannot_change_own_role, role_not_in_workspace, not_a_member · 429 slow down
security_access.set_reports_to
Set who a member reports to — Builds the line a “their own and their reports’” role follows.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_members/actions/set_reports_to- Canonical address:
POST /api/v1/actions/security_access.set_reports_to
Input
| Field | Type | Required | Notes |
|---|---|---|---|
user_id | uuid | yes | Member |
reports_to | uuid | Reports to · id of app_user |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
cannot_report_to_self | Someone cannot report to themselves. |
would_create_cycle | That would make two people report to each other. |
not_a_member | Both must be members of this workspace. |
Events it publishes: security_access.reports_to_changed
What it writes: workspace_member (update) — Sets the reporting line
curl -X POST https://app.getera.io/api/v1/workspace_members/actions/set_reports_to \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"user_id":"USER_ID"}'{
"ok": true,
"result": "…",
"events": [
"security_access.reports_to_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceMembers.setReportsTo({
user_id: "USER_ID"
})era workspace_members set_reports_to \
--user-id "USER_ID"{
"name": "era__security_access__set_reports_to",
"arguments": {
"user_id": "USER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 cannot_report_to_self, would_create_cycle, not_a_member · 429 slow down
security_access.set_role_channels
Limit a role to sales channels — Restricts shop access to selected channels; an empty selection grants no channels.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/roles/{id}/actions/set_role_channels- Canonical address:
POST /api/v1/actions/security_access.set_role_channels
Input
| Field | Type | Required | Notes |
|---|---|---|---|
role_id | uuid | yes | Filled from the path {id} · Role · id of role |
restricted | boolean | yes | Restrict channels |
channel_ids | object / array | Channels |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
system_role_protected | The owner role cannot be restricted. |
channel_scope_invalid | Every selected channel must belong to this workspace. |
Events it publishes: security_access.role_channels_changed
What it writes: role (update) — Sets the sales channel scope of this role
curl -X POST https://app.getera.io/api/v1/roles/ROLE_ID/actions/set_role_channels \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"restricted":true}'{
"ok": true,
"result": "…",
"events": [
"security_access.role_channels_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.roles.setRoleChannels("ROLE_ID", {
restricted: true
})era roles set_role_channels ROLE_ID \
--restricted true{
"name": "era__security_access__set_role_channels",
"arguments": {
"role_id": "ROLE_ID",
"restricted": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 system_role_protected, channel_scope_invalid · 429 slow down
security_access.toggle_feature
Switch part of a module on or off — Turns one part of a module on — appointments, group classes, packages.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_modules/actions/toggle_feature- Canonical address:
POST /api/v1/actions/security_access.toggle_feature
Input
| Field | Type | Required | Notes |
|---|---|---|---|
module_code | string | yes | Module · max 41 |
feature | string | yes | Part · max 41 |
enabled | boolean | yes | Enabled |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
module_not_enabled | The module itself is switched off. |
unknown_feature | That module has no such part. |
Events it publishes: security_access.feature_enabled, security_access.feature_disabled
What it writes: workspace_module (update) — Updates the enabled parts
curl -X POST https://app.getera.io/api/v1/workspace_modules/actions/toggle_feature \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"module_code":"Module","feature":"Part","enabled":true}'{
"ok": true,
"result": "…",
"events": [
"security_access.feature_enabled",
"security_access.feature_disabled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceModules.toggleFeature({
module_code: "Module",
feature: "Part",
enabled: true
})era workspace_modules toggle_feature \
--module-code "Module" \
--feature "Part" \
--enabled true{
"name": "era__security_access__toggle_feature",
"arguments": {
"module_code": "Module",
"feature": "Part",
"enabled": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 module_not_enabled, unknown_feature · 429 slow down
security_access.toggle_module
Switch a module on or off — Adds a module to this workspace, or takes it away without deleting its data.
Needs security_access:manage · removes or cannot be undone
https://app.getera.io/api/v1/workspace_modules/actions/toggle_module- Canonical address:
POST /api/v1/actions/security_access.toggle_module
Input
| Field | Type | Required | Notes |
|---|---|---|---|
module_code | string | yes | Module · max 41 |
enabled | boolean | yes | Enabled |
with_dependencies | boolean | Install what it needs too · Switches on every missing dependency first, in the same transaction. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
shell_module | This is part of the shell and is always on. |
missing_dependency | This module needs another one that is switched off. |
dependents_enabled | Other modules that are switched on depend on this one. |
module_limit_reached | The workspace must have room for the module and its dependencies on its plan. |
Events it publishes: security_access.module_enabled, security_access.module_disabled
What it writes: workspace_module (upsert) — Switches the module on or off
curl -X POST https://app.getera.io/api/v1/workspace_modules/actions/toggle_module \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"module_code":"Module","enabled":true}'{
"ok": true,
"result": "…",
"events": [
"security_access.module_enabled",
"security_access.module_disabled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workspaceModules.toggleModule({
module_code: "Module",
enabled: true
})era workspace_modules toggle_module \
--module-code "Module" \
--enabled true{
"name": "era__security_access__toggle_module",
"arguments": {
"module_code": "Module",
"enabled": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold security_access:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 shell_module, missing_dependency, dependents_enabled, module_limit_reached · 429 slow down
Data models
AccessGrant
Table access_grant · Resource access_grants
GET /api/v1/access_grantsGET /api/v1/access_grants/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | app_user | ||
subject_type | string · text | module, report, view | ||
subject_key | string · text | |||
capability | string · text | |||
row_scope | string · text | all, own_records, own_and_reports | ||
masked_fields | string[] · text[] | |||
granted_by | string (uuid) · uuid | yes | app_user | |
expires_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
channel_ids | string[] · uuid[] | yes |
Primary key: id
Unique: workspace_id, user_id, subject_type, subject_key, capability
BlueprintInstall
Table blueprint_install · Resource blueprint_installs
GET /api/v1/blueprint_installsGET /api/v1/blueprint_installs/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
blueprint_code | string · text | |||
version | integer · int4 | |||
modules | string[] · text[] | |||
applied_by_user_id | string (uuid) · uuid | yes | app_user | |
first_applied_at | string (date-time) · timestamptz | |||
applied_at | string (date-time) · timestamptz | |||
apply_count | integer · int4 | |||
choices | object · jsonb | yes | ||
sample_records | object · jsonb |
Primary key: id
Unique: workspace_id, blueprint_code
Checks:
CHECK ((apply_count > 0))CHECK ((blueprint_code ~ '^[a-z][a-z0-9_]{1,40}$'::text))CHECK (((choices IS NULL) OR (jsonb_typeof(choices) = 'object'::text)))CHECK ((jsonb_typeof(sample_records) = 'array'::text))CHECK ((version > 0))
RoleModuleCapability
Table role_module_capability · Resource role_module_capabilities
GET /api/v1/role_module_capabilitiesGET /api/v1/role_module_capabilities/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
role_id | string (uuid) · uuid | role | ||
module_code | string · text | module | ||
capability | string · text | read, write, delete, manage, approve, export, comment, handle, import, impersonate, own_records_only, computer_use, computer_control | ||
own_records_scope | string · text | self_only, self_and_reports |
Primary key: id
Unique: role_id, module_code, capability
Checks:
CHECK (((capability = 'own_records_only'::text) OR (own_records_scope = 'self_only'::text)))
Role
Table role · Resource roles
GET /api/v1/rolesGET /api/v1/roles/{id}POST /api/v1/roles→security_access.create_role
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
code | string · text | |||
label | string · text | |||
description | string · text | yes | ||
is_system | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
sort_order | integer · int4 | |||
updated_at | string (date-time) · timestamptz | |||
channel_ids | string[] · uuid[] | yes |
Primary key: id
Unique: workspace_id, code
Checks:
CHECK ((code ~ '^[a-z][a-z0-9_]{1,40}$'::text))
WorkspaceMember
Table workspace_member · Resource workspace_members
GET /api/v1/workspace_membersGET /api/v1/workspace_members/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | app_user | ||
role_id | string (uuid) · uuid | role | ||
created_at | string (date-time) · timestamptz | |||
status | string · text | active, suspended | ||
updated_at | string (date-time) · timestamptz | |||
reports_to | string (uuid) · uuid | yes | app_user | |
color | string · text | yes |
Primary key: id
Unique: workspace_id, user_id
Checks:
CHECK (((color IS NULL) OR (color ~ '^#[0-9A-Fa-f]{6}$'::text)))CHECK (((reports_to IS NULL) OR (reports_to <> user_id)))
WorkspaceModule
Table workspace_module · Resource workspace_modules
GET /api/v1/workspace_modulesGET /api/v1/workspace_modules/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
module_code | string · text | module | ||
enabled | boolean · bool | |||
sidebar_group | string · text | yes | ||
sidebar_order | integer · int4 | |||
enabled_at | string (date-time) · timestamptz | |||
enabled_features | string[] · text[] | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: workspace_id, module_code
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
security_access.access_granted | Access was granted to one person directly. | user_id, subject_type, subject_key |
security_access.access_revoked | A personal grant was revoked. | grant_id |
security_access.blueprint_applied | An industry blueprint was installed or re-applied. | blueprint_code, version, installed, first |
security_access.capability_granted | A capability was granted to a role. | role_id, module_code, capability |
security_access.capability_revoked | A capability was taken away from a role. | role_id, module_code, capability |
security_access.feature_disabled | Part of a module was switched off. | module_code, feature |
security_access.feature_enabled | Part of a module was switched on. | module_code, feature |
security_access.module_disabled | A module was switched off. | module_code |
security_access.module_enabled | A module was switched on for the workspace. | module_code |
security_access.reports_to_changed | A reporting line changed. | user_id, reports_to |
security_access.role_changed | A member’s role was changed. | user_id, role_id |
security_access.role_channels_changed | The sales channel scope of a role changed. | role_id, channel_ids |
security_access.role_created | A workspace role was created. | role_id, code |
security_access.role_deleted | A workspace role was deleted. | role_id, code |
security_access.shop_opened | The shop was opened in one step: shop modules on, a sales channel open. | channelId, name, currency, installed |