Kinesis
The /kinesis endpoint receives data from Amazon Data Firehose (formerly Kinesis Data Firehose). It implements the Firehose HTTP endpoint delivery request and response specification, so you can add Observe as an HTTP Endpoint destination on any Firehose stream.
| Setting | Value |
|---|---|
| Endpoint | /kinesis |
| URL | https://${OBSERVE_CUSTOMER}.collect.observeinc.com/v1/kinesis |
| Authentication | Datastream token, sent as the Firehose access key |
| Content type | application/json (the Firehose envelope) |
| Maximum request size | 4 MB |
When to use this endpoint
Use the Kinesis endpoint when your data already flows through Firehose, or when the source can only deliver to Firehose. Common cases:
- CloudWatch Logs subscription filters that deliver to a Firehose stream
- Applications or AWS services that write records to a Firehose stream or Kinesis data stream
- Third-party services that export to a customer-owned Firehose stream
To collect AWS logs and metrics across many accounts, start with the AWS integration instead. It configures the AWS side for you.
Prerequisites
- A Datastream and a datastream token for it. Copy the token when you create it; Observe only shows the secret once.
- Your Observe customer ID. See Where do I find my customer ID?.
- Permission to create or edit Amazon Data Firehose streams in your AWS account.
Configure the Firehose stream
- In the AWS console, open Amazon Data Firehose and create a stream, or edit an existing one.
- Set Destination to HTTP Endpoint.
- Set HTTP endpoint URL to
https://${OBSERVE_CUSTOMER}.collect.observeinc.com/v1/kinesis. To change how Observe decodes records, append query parameters. - Set Access key to your datastream token. Firehose sends it in the
X-Amz-Firehose-Access-Keyheader, and Observe uses it as the bearer token for the request. - Set Content encoding to GZIP. This compresses each request in transit and keeps large buffers under the request size limit.
- Optionally, add Parameters (key/value pairs). Observe adds them to every observation's
EXTRAcolumn, which is a convenient way to tag a stream, for exampleenvironment=prod. - Under Buffer hints, set Buffer size to 1 MiB. See Request size for why.
- Configure an S3 backup bucket for failed records, so data is not lost if delivery keeps failing past the retry duration.
Request size
Observe rejects requests larger than 4 MB. Firehose buffer sizes are measured before Firehose base64-encodes each record, which adds about a third to the payload, and Firehose allows buffer sizes far larger than 4 MB. To stay under the limit:
- Keep Buffer size at 1 MiB, and no more than 3 MiB.
- Enable GZIP content encoding.
If requests exceed the limit, Firehose reports delivery errors and eventually writes the records to your S3 backup bucket.
How records are decoded
Each Firehose request contains one or more records. Observe creates at least one observation per record:
-
Base64 decoding. Firehose base64-encodes every record. Observe decodes it.
-
Gzip decompression. Observe tries to gunzip each record. Records that are not gzip-compressed are kept as they are. CloudWatch Logs subscriptions gzip each record, so their data arrives as readable JSON without extra configuration. To keep compressed bytes as they are, use
option=passthrough. -
Splitting large records. A record larger than
maxlen(3 MiB by default) is split into several observations:- CloudWatch Logs payloads are split by
logEvents. Each piece keeps the originalowner,logGroup,logStream,subscriptionFilters, andmessageType, so every observation is valid CloudWatch JSON. - Newline-delimited JSON is split on line boundaries, so no JSON object is cut in half.
- Other text is split at a UTF-8 character boundary. Every piece after the first has
continueset totrue, so you can reassemble the text in OPAL.
Records sent with
option=binaryare never split. - CloudWatch Logs payloads are split by
Query parameters
Append these to the endpoint URL to change decoding:
| Parameter | Values | Default | Effect |
|---|---|---|---|
option | string, binary | string | string stores data as text. binary stores the raw bytes and turns off splitting. |
option | decompress, passthrough | decompress | decompress gunzips each record when possible. passthrough stores records as received. |
maxlen | bytes | 3145728 (3 MiB) | Records larger than this are split. 0 turns off splitting. Values from 1 to 15 are raised to 16. |
option can be repeated, for example ?option=binary&option=passthrough. When two values conflict, the last one wins.
Any other query parameter is added to EXTRA. For example, https://${OBSERVE_CUSTOMER}.collect.observeinc.com/v1/kinesis?source=checkout tags every observation from that stream with "source": "checkout".
Data format
Each observation has these fields:
| Column | Key | Description |
|---|---|---|
FIELDS | data | The decoded record, as a string (or binary with option=binary) |
FIELDS | requestId | The Firehose request ID. All records from one request share it. |
FIELDS | timestamp | The Firehose request timestamp, in milliseconds since the Unix epoch |
FIELDS | continue | Present and true on the second and later pieces of a split text record |
EXTRA | X-Amz-Firehose-Source-Arn | ARN of the Firehose stream that sent the request |
EXTRA | X-Amz-Firehose-Protocol-Version | Firehose protocol version |
EXTRA | your parameters | Firehose Parameters and extra URL query parameters |
For example, a record from a CloudWatch Logs subscription looks like this after decoding:
{
"data": "{\"messageType\":\"DATA_MESSAGE\",\"owner\":\"123456789012\",\"logGroup\":\"/aws/lambda/checkout\",\"logStream\":\"2026/11/03/[$LATEST]abc123\",\"subscriptionFilters\":[\"to-observe\"],\"logEvents\":[{\"id\":\"3812...\",\"timestamp\":1793750400000,\"message\":\"START RequestId: ...\"}]}",
"requestId": "ed4acda5-034f-9f42-bba1-f29aea6d7d8f",
"timestamp": 1793750401234
}To work with the payload, parse data in OPAL:
make_col payload:parse_json(string(FIELDS.data))
make_col logGroup:string(payload.logGroup), logStream:string(payload.logStream)
CloudWatch Logs also sends CONTROL_MESSAGE records to check that the destination is reachable. Filter them out with filter string(payload.messageType) = "DATA_MESSAGE".
Responses and retries
Observe answers each request in the Firehose response format. A successful delivery returns 200 OK:
{"requestId": "ed4acda5-034f-9f42-bba1-f29aea6d7d8f", "timestamp": 1793750401500}A failed delivery returns an error status with an errorMessage. Firehose shows the message in the stream's delivery error logs, retries for the configured retry duration, and then writes the records to your S3 backup bucket. Common errors:
| Status | Cause | Fix |
|---|---|---|
401 or 403 | No access key, or the key is not a valid datastream token | Set Access key to a datastream token |
413 | Request larger than 4 MB | Lower Buffer size and enable GZIP |
415 | Content type is not application/json | Send requests in the Firehose envelope format |
400 | Body is not a Firehose request, has no records, or contains invalid base64 | Check that the sender follows the Firehose HTTP endpoint specification |
Test the endpoint
You can send a request in the Firehose format with curl. Record data must be base64-encoded:
DATA=$(printf '{"message":"hello from firehose"}' | base64)
curl -sS "https://${OBSERVE_CUSTOMER}.collect.observeinc.com/v1/kinesis" \
-H "Content-Type: application/json" \
-H "X-Amz-Firehose-Access-Key: ${DATASTREAM_TOKEN}" \
-H "X-Amz-Firehose-Request-Id: test-1" \
-d "{\"requestId\":\"test-1\",\"timestamp\":$(date +%s000),\"records\":[{\"data\":\"${DATA}\"}]}"A successful response echoes the request ID: {"requestId":"test-1", "timestamp":...}. The observation appears in your datastream's dataset within a few minutes.
Updated about 4 hours ago