Organizations (Orgs)
Endpoints under the Orgs tag manage organizations (tenants) the caller can access: list and CRUD, permissions for the current principal, org-scoped user listing, soft delete, and hard purge. Response bodies follow OrganizationResponse and related schemas in the API reference.
Relationship to org context: Choosing X-Org for device and integration routes is separate from these APIs — see Organization context & pagination. Use GET /orgs to discover org ids when the account spans multiple organizations.
Operations vs wire routes
Section titled “Operations vs wire routes”| Concern | HTTP | operationId | Notes |
|---|---|---|---|
| List | GET /orgs | list_orgs | Requires output_options and pagination query objects on the wire (see bundle). |
| Create | POST /orgs | create_org | Body CreateOrganizationRequest (name required; optional description, parent_id, admin-only id). |
| Get | GET /orgs/{id} | get_org | |
| Update | PUT /orgs/{id} | update_org | Partial-style body per UpdateOrganizationRequest. |
| Soft delete | DELETE /orgs/{id} | delete_org | |
| Hard delete | DELETE /orgs/{id}/purge | hard_delete_org | |
| Caller permissions | GET /orgs/{id}/permissions | get_org_permissions | Permissions for the current user in that org. |
| Users in org | GET /orgs/{org_id}/users | list_org_users | Requires X-Org, output_options, pagination; optional recursive query. |
SDK coverage
Section titled “SDK coverage”| Capability | Python | Rust (openapp_sdk) | Go | TypeScript (AsyncClient) |
|---|---|---|---|---|
| List | client.orgs.list(...) — forwards output_options / pagination fields via the bridge | client.orgs().list() — bare GET /orgs without required query objects; use transport() + RequestSpec for OpenAPI parity | OrgsAPI.ListOrgs with full query builders | listOrgs — bare GET /orgs |
| Create | client.orgs.create(name=..., **extra) | client.orgs().create(&json!) | OrgsAPI.CreateOrg | createOrg(name) — sends { name } only |
| Get / update / delete / purge | get, update, delete, purge | get, update, delete, purge | Generated OrgsAPI | getOrg only — no update/delete helpers on façade |
| Permissions | permissions(org_id) | permissions(id) | GetOrgPermissions | Not on façade — use core transport or another SDK |
| List org users | users(org_id, ...) | users(org_id) — bare path; wire may require headers/queries | ListOrgUsers | Not on façade |
Python additionally exposes upload_image / upload_image_from_url for POST /orgs/{id}/image (multipart) where your deployment exposes that route; see Python — Resources.
Typical errors
Section titled “Typical errors”401 / 403 when the key cannot access the org.404 for unknown org ids.400 on create/update validation. Hard delete may fail when the server rejects purge preconditions. Shapes follow ApiErrorResponse — see Errors & retries.
Examples
Section titled “Examples”List organizations
Section titled “List organizations”client.orgs.list maps kwargs to the list_orgs query parameters (include_deleted, limit, cursor, …) expected by your deployment.
page = await client.orgs.list(limit=50)ListOrgs requires OutputOptions and Pagination.
import ( openapiclient "github.com/tomers/openapp-sdk/go")
resp, httpResp, err := client.OrgsAPI.ListOrgs(ctx). OutputOptions(*openapiclient.NewMultiResourceOutputOptionsQuery(false, false, false)). Pagination(openapiclient.PaginationQuery{ Limit: openapiclient.PtrInt32(50), Offset: openapiclient.PtrInt32(0), }). Execute()if err != nil { return err}defer httpResp.Body.Close()Thin client.orgs().list() omits required OpenAPI query bundles; use Transport with a RequestSpec when you need list_orgs parity (same idea as Organization context & pagination).
use openapp_sdk::Client;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
let orgs = client.orgs().list().await?;import { AsyncClient } from "@tomers/openapp-sdk";
const client = new AsyncClient("https://api.openapp.house/api/v1_openapp_YOUR_SECRET");const raw = await client.listOrgs();The façade does not yet mirror output_options / pagination on GET /orgs — prefer Python or Go when you need full wire parity.
Create an organization
Section titled “Create an organization”org = await client.orgs.create(name="Field Office", parent_id="01HPARENT000000000000000000")import ( openapiclient "github.com/tomers/openapp-sdk/go")
name := *openapiclient.NewLocalizedString(map[string]string{"en": "Field Office"})body := openapiclient.NewCreateOrganizationRequest(name)body.SetParentId("01HPARENT000000000000000000")
resp, httpResp, err := client.OrgsAPI.CreateOrg(ctx).CreateOrganizationRequest(*body).Execute()if err != nil { return err}defer httpResp.Body.Close()use openapp_sdk::Client;use serde_json::json;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
let org = client .orgs() .create(&json!({ "name": { "en": "Field Office" }, "parent_id": "01HPARENT000000000000000000" })) .await?;import { AsyncClient } from "@tomers/openapp-sdk";
const client = new AsyncClient("https://api.openapp.house/api/v1_openapp_YOUR_SECRET");const raw = await client.createOrg("Field Office");Use Python, Rust, or Go when you need description, parent_id, or other CreateOrganizationRequest fields beyond name.
Get / update / delete / purge (/orgs/{id})
Section titled “Get / update / delete / purge (/orgs/{id})”update_org accepts an UpdateOrganizationRequest (optional name as LocalizedString, description, parent_id). delete_org is reversible at the data layer; hard_delete_org removes the row irrecoverably.
org = await client.orgs.get(org_id)updated = await client.orgs.update(org_id, name={"value": {"en": "Field Office (renamed)"}})await client.orgs.delete(org_id)await client.orgs.purge(org_id)import ( "context"
openapiclient "github.com/tomers/openapp-sdk/go")
org, httpResp, err := client.OrgsAPI.GetOrg(context.Background(), orgID).Execute()if err != nil { return err}defer httpResp.Body.Close()_ = org
upd := openapiclient.NewUpdateOrganizationRequest()upd.SetName(*openapiclient.NewLocalizedString(map[string]string{"en": "Field Office (renamed)"}))updated, httpResp2, err := client.OrgsAPI.UpdateOrg(context.Background(), orgID). UpdateOrganizationRequest(*upd). Execute()if err != nil { return err}defer httpResp2.Body.Close()_ = updated
_, httpResp3, err := client.OrgsAPI.DeleteOrg(context.Background(), orgID).Execute()if err != nil { return err}defer httpResp3.Body.Close()
_, httpResp4, err := client.OrgsAPI.HardDeleteOrg(context.Background(), orgID).Execute()if err != nil { return err}defer httpResp4.Body.Close()use openapp_sdk::Client;use serde_json::json;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
let org = client.orgs().get(org_id).await?;let updated = client .orgs() .update( org_id, &json!({ "name": { "value": { "en": "Field Office (renamed)" } } }), ) .await?;client.orgs().delete(org_id).await?;client.orgs().purge(org_id).await?;import { AsyncClient } from "@tomers/openapp-sdk";
const client = new AsyncClient( "https://api.openapp.house/api/v1_openapp_YOUR_SECRET",);
const org = await client.getOrg(orgId);
const updated = await fetch(`https://api.openapp.house/api/v1/orgs/${orgId}`, { method: "PUT", headers: { authorization: "Bearer v1_openapp_YOUR_SECRET", "content-type": "application/json", }, body: JSON.stringify({ name: { value: { en: "Field Office (renamed)" } }, }),}).then((r) => r.json());
await fetch(`https://api.openapp.house/api/v1/orgs/${orgId}`, { method: "DELETE", headers: { authorization: "Bearer v1_openapp_YOUR_SECRET" },});await fetch(`https://api.openapp.house/api/v1/orgs/${orgId}/purge`, { method: "DELETE", headers: { authorization: "Bearer v1_openapp_YOUR_SECRET" },});The Node façade ships getOrg but not the mutation helpers — pair it with raw fetch for PUT|DELETE /orgs/{id} and DELETE /orgs/{id}/purge. UpdateOrganizationRequest wraps name in LocalizedString ({ value: { en: "…" } }); soft delete is reversible until /purge runs. Replace each fetch with AsyncClient lifecycle helpers once the Node façade exposes them.
Caller permissions and org members
Section titled “Caller permissions and org members”get_org_permissions returns the permissions held by the calling principal in that org (use it to drive UI gating). list_org_users enumerates members and accepts an optional recursive flag to include descendant orgs; the route requires X-Org plus output_options + pagination on the wire.
perms = await client.orgs.permissions(org_id)members = await client.orgs.users(org_id, limit=50)For recursive parity with list_org_users, send the request via AsyncClient._request with the explicit query key.
perms, httpResp, err := client.OrgsAPI.GetOrgPermissions(ctx, orgID).Execute()if err != nil { return err}defer httpResp.Body.Close()_ = perms
members, httpResp2, err := client.OrgsAPI.ListOrgUsers(ctx, orgID). XOrg(orgID). OutputOptions(*openapiclient.NewMultiResourceOutputOptionsQuery(false, false, false)). Pagination(openapiclient.PaginationQuery{ Limit: openapiclient.PtrInt32(50), Offset: openapiclient.PtrInt32(0), }). Recursive(true). Execute()if err != nil { return err}defer httpResp2.Body.Close()_ = membersuse openapp_sdk::Client;
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
let perms = client.orgs().permissions(org_id).await?;let members = client.orgs().users(org_id).await?;users() issues a bare GET /orgs/{id}/users; build a RequestSpec on client.transport() when you need X-Org, output_options, pagination, or recursive=true.
const perms = await fetch( `https://api.openapp.house/api/v1/orgs/${orgId}/permissions`, { headers: { authorization: "Bearer v1_openapp_YOUR_SECRET" } },).then((r) => r.json() as Promise<{ permissions: string[] }>);
const usersUrl = new URL(`https://api.openapp.house/api/v1/orgs/${orgId}/users`);usersUrl.searchParams.set("limit", "50");usersUrl.searchParams.set("offset", "0");usersUrl.searchParams.set("include_deleted", "false");usersUrl.searchParams.set("recursive", "true");
const members = await fetch(usersUrl, { headers: { authorization: "Bearer v1_openapp_YOUR_SECRET", "x-org": orgId, },}).then((r) => r.json() as Promise<{ items: Array<{ id: string }> }>);/permissions returns the calling principal’s permissions in that org (use it to gate UI). /users requires X-Org plus the flat output_options / pagination keys; pass recursive=true to include descendant orgs. Replace each fetch with AsyncClient helpers once the Node façade exposes them.