Release a locker compartment via API
Parcel lockers and mailroom matrices release one compartment at a time when a recipient or courier is authorized. In OpenApp each compartment is modeled as a switchable entity — the same switchable.open action used for doors and gates.
This guide is for campus mail services, corporate mailrooms, and parcel-wall operators automating pickup. See Model by sector (locker / parcel matrix).
- One integration + devices represent the locker bank (or one device per compartment).
- Each compartment is an entity with a human-readable name in metadata.
- Classify each compartment’s device. For a
switchcompartment entity, Not an access opening (locker, light) is enough; adoorentity must use an access opening class and attestation. Until the device is classified, opens return409 life_safety_class_required. - Your app validates a pickup code, then calls
switchable.openon that entity only.
Release one compartment
Section titled “Release one compartment”SDK
await client.entities.actions(entity_id, "switchable.open")const res = await fetch( `${apiBase}/entities/${entityId}/actions/switchable.open`, { method: "POST", headers: { "x-api-key": apiKey, "content-type": "application/json", "x-org": orgId, }, body: JSON.stringify({}), },);if (!res.ok) throw new Error(`open failed: ${res.status}`);use openapp_sdk::Client;use serde_json::json;
let client = Client::builder() .api_key("https://openapp.house_openapp_YOUR_SECRET") .build()?;
client .entities() .invoke_action(entity_id, "switchable.open", &json!({})) .await?;httpResp, err := client.EntitiesAPI.ExecuteEntityAction(ctx, entityID, "switchable.open"). Body(map[string]interface{}{}). Execute()if err != nil { return err}defer httpResp.Body.Close()HTTP API (curl)
export OPENAPP_API_BASE='https://openapp.house/api/v1'export OPENAPP_API_KEY='https://openapp.house_openapp_YOUR_SECRET'export OPENAPP_ORG_ID='01HORG00000000000000000000'export COMPARTMENT_ENTITY_ID='01HENTITY000000000000000000'
curl -sS -X POST \ -H "X-API-Key: ${OPENAPP_API_KEY}" \ -H "Content-Type: application/json" \ -H "X-Org: ${OPENAPP_ORG_ID}" \ -d '{}' \ "${OPENAPP_API_BASE}/entities/${COMPARTMENT_ENTITY_ID}/actions/switchable.open"Discover compartment entities
Section titled “Discover compartment entities”SDK
rows = await client.integrations.entities(integration_id)const url = new URL( `${apiBase}/integrations/${integrationId}/entities`,);url.searchParams.set("limit", "100");url.searchParams.set("offset", "0");
const page = await fetch(url, { headers: { "x-api-key": apiKey, "x-org": orgId, },}).then((r) => r.json());use openapp_sdk::Client;
let client = Client::builder() .api_key("https://openapp.house_openapp_YOUR_SECRET") .build()?;
let rows = client.integrations().entities(integration_id).await?;page, httpResp, err := client.IntegrationsAPI.ListIntegrationEntities(ctx, integrationID). XOrg(orgID). OutputOptions(*openapiclient.NewMultiResourceOutputOptionsQuery()). Pagination(openapiclient.PaginationQuery{ Limit: openapiclient.PtrInt32(100), Offset: openapiclient.PtrInt32(0), }). Execute()if err != nil { return err}defer httpResp.Body.Close()_ = pageHTTP API (curl)
export INTEGRATION_ID='01HINTEGRATION00000000000000'
curl -sS \ -H "X-API-Key: ${OPENAPP_API_KEY}" \ -H "X-Org: ${OPENAPP_ORG_ID}" \ "${OPENAPP_API_BASE}/integrations/${INTEGRATION_ID}/entities?limit=100&offset=0"Map external ids (compartment numbers) to entity ids during provisioning — provision a building with scripting for bulk setup.
Safety
Section titled “Safety”- Never open by numeric guess — resolve entity id from your parcel system’s authoritative mapping.
- Rate-limit automated release endpoints exposed to couriers.
- Each release is audited as
entity.action.succeeded/entity.action.denied; keep the correlation id from failures to join your logs to it (audit activity).