Admin App Bridge and the Store API have separate responsibilities. The bridge connects your embedded app to the Tringify admin. When the screen needs store data, it requests a short-lived Store API session containing only the scopes needed for that work.
Request a session token#
bridge.getSessionToken(options) returns Store API access for the requested scope set. scopes is required and must contain at least one valid scope.
Option |
Type |
Required |
Meaning |
|---|---|---|---|
scopes |
string[] |
yes |
The smallest Store API scope set needed for the related screen or action. Empty arrays and blank values are rejected. |
The promise resolves to { token, scopes, expiresAt }. scopes contains the scopes granted to this session and expiresAt is an ISO 8601 timestamp.
const session = await bridge.getSessionToken({
scopes: ["read_products"],
});
const response = await fetch(
"https://api-store.tringify.com/api/2025-01/products",
{ headers: { Authorization: `Bearer ${session.token}` } },
);
const body = await response.json();
if (!response.ok) {
throw new Error(body.error.message);
}
const products = body.data.products;
How access is decided#
A requested scope is available only when every applicable permission boundary allows it:
Your app declared the scope in the Developer Portal.
The merchant approved that scope for this installation.
The user who opened the app is permitted to perform the corresponding work.
The current screen explicitly requested the scope.
This means an embedded app cannot gain access merely because the store owner installed it, and a staff member cannot use the app to reach data outside their own role.
Request scopes per screen#
Ask for the scopes needed by the current unit of work, not every scope approved for the app. For example, a product picker may need read_products, while a product editor may need write_products. A write scope satisfies the matching read scope, so a screen that edits products only needs write_products.
async function loadProducts() {
const { token } = await bridge.getSessionToken({
scopes: ["read_products"],
});
return fetchProducts(token);
}
async function saveProduct(input) {
const { token } = await bridge.getSessionToken({
scopes: ["write_products"],
});
return updateProduct(token, input);
}
Let the SDK manage the session#
Call getSessionToken() before each related batch of Store API work. The SDK safely reuses a valid session for the same scope set and renews credentials when necessary.
Do not save the token in local storage, cookies, your database, or analytics.
Do not send the token to another browser, store, or app installation.
Do not implement your own refresh timer.
Discard the token when the embedded page closes.
Handle unavailable permissions#
A token request is rejected with APP_OAUTH_SCOPE_NOT_GRANTED when the scope is outside what this session may use — the app did not declare it, the installation has not approved it, or the current user lacks the corresponding permission — and with APP_OAUTH_INVALID_SCOPE when the scope name is not a valid Store API scope. Keep the screen usable where possible and explain which capability is unavailable; treat other rejections as the errors they are.
try {
const session = await bridge.getSessionToken({
scopes: ["write_products"],
});
showProductEditor(session.token);
} catch (error) {
if (error.code === "APP_OAUTH_SCOPE_NOT_GRANTED") {
showReadOnlyState(
"You do not have permission to edit products in this store.",
);
return;
}
throw error;
}
Handle contract errors#
Bridge failures include a stable error.code and a readable error.message. Use the code to choose application behavior, and use the message for logs or a helpful developer-facing diagnostic. Invalid input is rejected rather than changed or guessed for you.
Code |
Meaning |
|---|---|
INVALID_ARGUMENT |
A required value is missing, an unknown field was supplied, or a value does not match the contract. |
INCOMPATIBLE_BRIDGE_MAJOR |
The page loaded an SDK major that does not match the host session. |
HOST_FRAME_REQUIRED |
The page was opened outside a Tringify embedded-app host. |
HOST_NOT_READY |
A host operation was called before |
UNSUPPORTED_OPERATION |
The current host does not provide the requested operation. |
UNSUPPORTED_EVENT |
The event name is not declared by this App Bridge version. |
HOST_TIMEOUT |
The host did not answer a short operation within its response window. |
INVALID_HOST_STATE |
The SDK cannot safely address the current host session. |
INVALID_HOST_RESPONSE |
The host response does not match the declared contract. |
INCOMPATIBLE_BRIDGE_CONTRACT |
The SDK and host fingerprints do not describe the same contract. |
LAUNCH_TOKEN_SCRUB_FAILED |
The SDK could not remove the launch credential from the browser URL and stopped. |
NETWORK_ERROR |
The Store API could not be reached during session exchange. |
INVALID_API_RESPONSE |
The Store API session response did not match its contract. |
try {
const session = await bridge.getSessionToken({
scopes: ["read_products"],
});
await loadProducts(session.token);
} catch (error) {
if (error.code === "INVALID_ARGUMENT") {
reportDeveloperConfigurationError(error);
return;
}
showTemporaryError("Products are unavailable right now.");
}
Browser access and backend access#
Credential |
Where it belongs |
Use it for |
|---|---|---|
Store API session token |
The embedded browser page |
Short-lived Store API calls made for the merchant currently using the app. |
Installation credential |
Your secured backend |
Background Store API work for that app installation, including work triggered by webhooks or scheduled jobs. |