Use Your Own AI Agent to Talk to Your Products with Notehub IQ

Blues Developers
What’s New
Resources
Blog
Technical articles for developers
Connected Product Guidebook
In-depth guides for connected product development
Developer Certification
Get certified on wireless connectivity with Blues
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Webinars
Listing of Blues technical webinars
Blues.comNotehub.io
Shop
Docs
Button IconHelp
Support DocsNotehub StatusVisit our Forum
Button IconSign In
What’s New
Resources
Blog
Technical articles for developers
Connected Product Guidebook
In-depth guides for connected product development
Developer Certification
Get certified on wireless connectivity with Blues
Newsletter
The monthly Blues developer newsletter
Terminal
Connect to a Notecard in your browser
Webinars
Listing of Blues technical webinars
Blues.comNotehub.io
Shop
Docs
homechevron_rightBlogchevron_rightKeep API Keys Off the Device: Use Authenticated Web Calls via Proxy Routes

Keep API Keys Off the Device: Use Authenticated Web Calls via Proxy Routes

Keep API Keys Off the Device: Use Authenticated Web Calls via Proxy Routes banner

August 26, 2026

Stop including secret keys on your devices! Notehub proxy routes let your credentials live in the cloud while the device stays anonymous.

  • Notecard
  • Notehub
  • Security
  • JSONata
Rob Lauer
Rob LauerSenior Director of Developer Relations
email

Say you're building out a fleet of devices that shift some functionality based on real-time data derived from an external REST API. The problem is that API requires a bearer token, and you have 4,000 devices in the field. Somewhere in your firmware, a line like this starts to look inevitable:

#define GRID_API_KEY "sk_live_8f2c…"

It works, but it also means that credential now exists in 4,000 places you don't control, in flash memory a bad actor can read with a $20 programmer, and rotating it requires an OTA firmware update.

There's a better solution! Blues Notecard has always been able to make HTTP calls to third-party services via Web Transactions. But you may not realize it can also make authenticated calls where the secure credentials live in Notehub and are never stored on the device.

Two approaches side by side. On the left, the credential is compiled into the firmware image, so every one of 4,000 deployed devices carries its own copy and rotating it requires an OTA firmware campaign. On the right, the credential is a single encrypted Notehub project secret referenced by placeholder, the devices carry no credential at all, and rotating it changes nothing on the devices.

Why Notecard Can't Just Call the API Directly

To clarify a common misconception, Notecard is deliberately restricted in who it can talk to. It has no publicly-accessible IP address, it doesn't listen for inbound connections, and its outbound TLS connection goes to exactly one place: Notehub. This is a real security advantage rather than a limitation!

The tradeoff is that Notecard can't open a socket to api.gridoperator.com on its own. Long ago we added a path that enables Notecard to make those calls: the web.* APIs combined with a Notehub proxy route.

The device asks Notehub to make the request on its behalf. Notehub holds the URL, the TLS trust, and the credentials, then performs the HTTP call, and hands the response back down the existing device connection. From the third-party API's perspective it's being called by Notehub. From the device's perspective it made a web request. Neither one needs to know about the other's authentication.

tip

We offer a good walkthrough of the mechanics in How to Call Third-Party Web Services From Notecard if you want an end-to-end explanation. What I want to focus on in this article is the part that matters once you're past a prototype phase. Specifically, where the secrets live and how to keep the response small enough to be useful on a constrained host.

A View of the Round Trip

Here's a look at what the full workflow of using a proxy route looks like:

  1. The device issues a web.get, web.post, or web.put request, naming a route alias that corresponds to a proxy route in Notehub.
  2. Notehub resolves the alias, applies an optional outbound JSONata transform to build the request body, substitutes placeholder variables into the URL and headers, and performs the HTTPS call.
  3. The third-party API responds to Notehub.
  4. Notehub applies an optional inbound JSONata transform and returns the result to the device, with the HTTP status code in a result field.

A host MCU and Notecard issue a web.get naming only a route alias, with no URL and no credential. Notehub resolves the alias, applies an outbound JSONata transform, substitutes the encrypted project secret into the Authorization header, and calls the third-party API over HTTPS. The API returns 385 bytes of raw JSON to Notehub, where an inbound response transform reduces it to 56 bytes before it travels back down to the device.

Let's look at this entire process in more detail:

Step 1: Put the Credential in Notehub, Not the Route

Notehub offers an encrypted Project Secrets store and it's the right home for any mission-critical secrets.

Under Settings → Secrets in your Notehub project, create a key/value pair. Name it something you'll recognize in a route definition, like GRID_API_KEY. Secret values are encrypted at rest, decrypted in-memory only at the instant a route fires, and are never returned by the Notehub API or shown in the UI after you save them. They also stay out of event history and route logs.

note

Notehub also supports environment variables with a similar [$name] placeholder syntax, and they work fine for non-sensitive configuration like a region code or a customer ID. But environment variables are stored and displayed as plaintext. Secure credentials on the other hand belong in Secrets.

Step 2: Create the Proxy Route

In your Notehub project, go to Routes, click Create Route, and select Proxy for Notecard Web Requests. You'll fill in:

  • Route name: whatever you want to see in the routes list in your Notehub project.
  • URL: the base host, e.g. https://api.gridoperator.com.
  • Alias: the short string your firmware will reference, e.g. grid-prices.

Next, open the HTTP Headers section, choose Additional Headers, and add the credential as a header whose value is a secret placeholder:

Authorization: Bearer [$$GRID_API_KEY]

Placeholder variables use square brackets, and the $$ prefix specifically means "look this up in my Notehub project secrets". Notehub substitutes the real value as the request leaves its infrastructure. Secrets work in the route's URL and header fields, which covers the two places REST APIs actually look for credentials.

note

Secrets cannot be used inside the request body or inside a JSONata transform expression. If you're integrating an API that demands its key in a JSON body field, this constraint can be an issue and you'll want to put a small function of your own in front of it rather than trying to work around it.

Step 3: Make the Request from Firmware

Two conditions must be met before a web.* request that hands a response back to your host can execute:

  1. Notecard must be in continuous mode.
  2. Notecard must actually be connected to Notehub.

You can go permanently continuous with a hub.set request that includes the "mode":"continuous" argument, but on a battery-powered device you'll likely want to open a temporary window with the "on":true argument:

{
  "req": "hub.set",
  "mode": "periodic",
  "on": true
}

Next, confirm you're online with a hub.status request

{"req": "hub.status"}

When the response returns {"connected":true}, you can make your web.* request.

Note what isn't in this request: no hostname, no key, and no certificate!

{
  "req": "web.get",
  "route": "grid-prices",
  "name": "/v1/prices/current?zone=NYIS"
}

The name argument is the path relative to the route's configured URL, query parameters included.

Using the note-arduino library, reading the result can look something like this:

J *req = notecard.newRequest("web.get");
if (req != NULL) {
    JAddStringToObject(req, "route", "grid-prices");
    JAddStringToObject(req, "name", "/v1/prices/current?zone=NYIS");

    J *rsp = notecard.requestAndResponse(req);
    if (rsp != NULL) {
        if (!notecard.responseError(rsp) && JGetInt(rsp, "result") == 200) {
            J *body = JGetObject(rsp, "body");
            if (body != NULL) {
                double price = JGetNumber(body, "price");
                bool peak = JGetBool(body, "peak");
                setDutyCycle(price, peak);
            }
        }
        notecard.deleteResponse(rsp);
    }
}

The result field carries the HTTP status code, body carries a JSON response, and non-JSON responses arrive base64-encoded in payload instead.

Step 4 (Optional): Shrink the Response

This is a completely optional step that can be incredibly helpful on constrained devices. Let's say the above mocked up API returns something like this:

{
  "request_id": "0f8c2a91-4b6e-4f2a-9a11-77c3e5d1b204",
  "status": "ok",
  "meta": {
    "zone": "NYIS",
    "currency": "USD",
    "units": "$/MWh",
    "generated_at": "2026-08-25T14:05:03Z",
    "api_version": "2.4.1",
    "rate_limit": { "limit": 1000, "remaining": 987, "reset": 1756137600 }
  },
  "intervals": [
    {
      "start": "2026-08-25T14:00:00Z",
      "end": "2026-08-25T14:05:00Z",
      "lmp": 128.4471,
      "congestion": 4.11,
      "losses": 1.92,
      "energy": 122.42
    }
  ]
}

That's 385 bytes to deliver one number your device cares about. Paying cellular data to move a rate_limit object to a microcontroller, and then spending that microcontroller's RAM parsing it, is a bad trade at any fleet size.

Using Inbound JSONata Transforms

Thankfully, the Notehub proxy route has an Inbound Response Transform field that takes a JSONata expression. It evaluates against the raw upstream response body, and whatever it returns becomes the body the device sees:

{
  "price": $round(intervals[0].lmp, 2),
  "peak": intervals[0].lmp > 100,
  "at": intervals[0].start
}

385 bytes becomes 56:

{
  "result": 200,
  "body": {
    "price": 128.45,
    "peak": true,
    "at": "2026-08-25T14:00:00Z"
  }
}
tip

Notice that the transform sees only the upstream body. The result envelope is applied around whatever your expression produced, so don't write your JSONata expecting a result field to be present.

Using Outbound JSONata Transforms

Note that there's a matching outbound transform for web.post and web.put, and it works a little differently. It evaluates against the whole Notehub event, not just the body your Notecard sent. So a device can send a minimal body and let Notehub enrich it with what it already knows about the device, like its DeviceUID, serial number, and the best_* location fields Notehub resolved.

$merge([body, {"device_id": device}])

$merge combines objects, so this one reads as "everything the device sent, plus one more field". A web.post body of {"level_cm":184.62,"pump_running":true} arrives upstream as:

{
  "level_cm": 184.62,
  "pump_running": true,
  "device_id": "dev:864475040261234"
}

Summary

Reach for a proxy route in Notehub when your device needs a response from a third-party API to act on. This could be any real-time data, a configuration lookup, a work-order status check, a "should I run right now?" decision, or anything else that provides important context for your product. And if you find yourself needing a secure credential in the request, just follow our advice in this article and you'll be well on your way.

To go deeper, consult the Web Transactions walkthrough which also covers queued transactions and large binary uploads, and the web.* API reference has the full list of APIs and arguments.

Happy hacking! 💙

Frequently Asked Questions

How do I call a third-party API from a Notecard without storing the API key on the device?

Create a "Proxy for Notecard Web Requests" route in Notehub, store the credential as a Notehub Project Secret, and reference it from the route's HTTP headers using the [$$SECRET_NAME] placeholder syntax. The device then issues a web.get, web.post, or web.put request naming only the route's alias. Notehub attaches the credential as the request leaves its infrastructure, so the key never exists in firmware, in flash, or on the wire between the device and Notehub.

Where can Notehub project secrets be used in a route?

Project secret placeholders work in the open text fields of a route definition, including the URL and HTTP headers. They cannot be used inside the request body or inside JSONata transform expressions. Since virtually every REST API expects its credential in an Authorization header or a query string parameter, the header and URL fields cover the common cases.

What is an Inbound Response Transform on a Notehub proxy route?

It is a JSONata expression that rewrites the third-party API's response before Notehub returns it to Notecard. The expression evaluates against the raw upstream response body, and whatever it returns becomes the body the device receives. It is the cleanest way to reduce a verbose REST response down to the two or three fields a memory-constrained host MCU actually needs, which also cuts the cellular data spent receiving it.

Do web transactions work on every Notecard?

The web.* APIs are available on Notecard Cellular, Notecard for Skylo, Notecard WiFi, and Notecard Cell+WiFi devices. They are not supported on Notecard for LoRa, nor while a device is operating in NTN mode. Web transactions also require an active connection to Notehub, which means continuous mode or a temporary window opened with hub.set.

In This Article

  • Why Notecard Can't Just Call the API Directly
  • A View of the Round Trip
  • Step 1: Put the Credential in Notehub, Not the Route
  • Step 2: Create the Proxy Route
  • Step 3: Make the Request from Firmware
  • Step 4 (Optional): Shrink the Response
    • Using Inbound JSONata Transforms
    • Using Outbound JSONata Transforms
  • Summary
  • Frequently Asked Questions

Blues Developer News

The latest IoT news for developers, delivered right to your inbox.

Comments

Join the conversation for this article on our Community Forum

Blues Developer Newsletter

The latest IoT news for developers, delivered right to your inbox.

© 2026 Blues Inc.
© 2026 Blues Inc.
TermsPrivacy