Discover
Read the public channel list, per-message pricing and site currency before choosing a configured route.
GET /api/v1/channelsGranli for developers
Make a request, observe its lifecycle and keep the customer experience inside your own product. Granli handles the routes behind it.
curl $ONEHUB_API_URL/api/v1/messages \
-H "Authorization: Bearer $GRANLI_KEY" \
-H "x-onehub-site: $ONEHUB_SITE" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"to": "+14155550123",
"body": "Your code is 482019"
}'globalThis.fetch()GRANLI_API_KEY=••••••••new URL("/api/v1/messages", process.env.ONEHUB_API_URL)Request lifecycle
Move from public channel discovery to authenticated message creation, then inspect the resulting record by ID.
Read the public channel list, per-message pricing and site currency before choosing a configured route.
GET /api/v1/channelsKeep a session token or API key on the server and send it with the Bearer authorization scheme.
Authorization: Bearer ...Submit a channel, recipient, and either a body or templateKey. A successful request returns a message record.
POST /api/v1/messages -> 201Use the returned message identity to request the current message detail within the same site scope.
GET /api/v1/messages/{id}Core resources
Use consistent resource shapes across messaging, templates, channels and delivery inspection.
/api/v1/messagesSubmit one transactional message to a configured channel.
/api/v1/messages/batchCreate multiple messages while preserving item-level outcomes.
/api/v1/messages/{id}Read the current lifecycle and request context for one signal.
/api/v1/channelsRead public availability, unit cost and site currency.
/api/v1/templatesManage reusable approved content with explicit variables.
Response handling
The API exposes message lookup, structured errors, request IDs and rate-limit headers so applications can react without guessing.
201 CreatedThe API returned a message record with delivery status, segment count and cost.Store its returned identity.GET /messages/{id}The message belongs to the current site and authenticated sender.Read the latest message detail.404 not_foundThe site has no matching message, or it does not belong to the caller.Do not infer global existence.429 rate_limitedThe caller exceeded the documented request limit.Wait for Retry-After before retrying.{
"error": {
"code": "rate_limited",
"message": "Too many requests",
"requestId": "req_8f31a"
},
"message": "Too many requests"
}Keep the x-request-id value when investigating a failure
Credential discipline
Keep credentials on the server, separate environments and connect each action to a request identifier.
Never place API secrets in public bundles or mobile application code.
Separate development and production access, credentials and traffic.
Read structured error codes and Retry-After instead of blindly replaying an unknown outcome.
Retain request IDs while showing safe, localized messages to customers.
Production checklist
Before moving traffic, validate behavior at both the API boundary and the final market.
Required registrations, templates and branded identities are active per market.
The product records the lawful basis and opt-out state for each recipient.
Success, validation, authentication, not-found and rate-limit responses are exercised safely.
Channel changes respect customer preference, urgency and local rules.
Product, support and network teams know who responds to each failure class.
Make the first request useful
Create an account for access or review the integration path with a Granli specialist.