> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peeng.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/logs — Batch Ingest Structured Log Entries

> Send 1–500 structured log entries in a single batch request. Supports idempotent retries and accepts level, message, metadata, stack traces, and more.

`POST /v1/logs` is the single ingestion endpoint for the Peeng API — it accepts a batch of 1 to 500 structured log entries in one request and is the same endpoint every Peeng SDK method calls under the hood. You can use it directly with any HTTP client if you prefer to stay dependency-free.

## Headers

<ParamField header="x-api-key" type="string" required>
  Your project's API key. See [Authentication](/authentication) for how to obtain and manage keys.
</ParamField>

<ParamField header="Idempotency-Key" type="string">
  A key unique to this batch. Safe to reuse on retries for up to 24 hours — Peeng returns the original response without writing duplicate entries. See [Idempotency](/essentials/idempotency).
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

## Request Body

<ParamField body="logs" type="array" required>
  An array of 1 to 500 log entry objects. Every entry in the array is validated; if any entry fails validation the entire request is rejected with a `400` and nothing is written.

  <Expandable title="Log entry fields">
    <ParamField body="level" type="string" required>
      Severity level of the log entry. Must be one of `debug`, `info`, `warn`, `error`, or `fatal`.
    </ParamField>

    <ParamField body="message" type="string" required>
      The human-readable log message describing the event.
    </ParamField>

    <ParamField body="environment" type="string" required>
      The environment that emitted this log — for example `production`, `staging`, or `development`. Use any string you apply consistently across your services so you can filter by environment in the dashboard.
    </ParamField>

    <ParamField body="statusCode" type="integer" required>
      A value between `100` and `599` that models the outcome of the operation this log describes — `200` for a routine success, `500` for an unhandled error, and so on. This is not tied to the HTTP status code of the ingestion request itself.
    </ParamField>

    <ParamField body="service" type="string">
      The name of the service or process that emitted this log, e.g. `checkout-api`. Use this to group and filter logs by service in the dashboard.
    </ParamField>

    <ParamField body="metadata" type="object">
      Any JSON-serializable object containing extra structured context for this log entry — for example request parameters, feature flags, or business identifiers.
    </ParamField>

    <ParamField body="stackTrace" type="string">
      The full stack trace text associated with an error. Providing this field enables the **Explain with AI** feature in the Peeng dashboard, which summarises the root cause and suggests a fix.
    </ParamField>

    <ParamField body="hostname" type="string">
      The hostname or instance identifier of the machine that emitted the log. Useful for pinpointing issues on a specific node in a distributed system.
    </ParamField>

    <ParamField body="requestId" type="string">
      A request or trace ID that correlates this log entry with the inbound request that triggered it. Use this to reconstruct a request's full journey across services.
    </ParamField>

    <ParamField body="userId" type="string">
      The identifier of the end user this log pertains to, if applicable. Lets you search and filter logs by user in the dashboard.
    </ParamField>

    <ParamField body="timestamp" type="string">
      The time the event occurred, in ISO 8601 format (e.g. `2025-01-15T10:30:00.000Z`). If you omit this field, Peeng stamps the entry with the time the request was received. Supply an explicit timestamp when you buffer and flush entries asynchronously.
    </ParamField>
  </Expandable>
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.peeng.dev/v1/logs \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Idempotency-Key: 5b3f9e2a-6c1d-4e8a-9f2b-1a3c5d7e9f0b" \
    -d '{
      "logs": [
        {
          "level": "error",
          "message": "Payment webhook failed",
          "environment": "production",
          "service": "billing",
          "statusCode": 500,
          "stackTrace": "Error: timeout\n    at processPayment (billing.js:42)",
          "requestId": "req_8f2a1c",
          "metadata": { "orderId": "order_789", "provider": "stripe" }
        },
        {
          "level": "info",
          "message": "Order placed",
          "environment": "production",
          "service": "checkout",
          "statusCode": 200,
          "userId": "user_456"
        }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 202 Accepted theme={null}
  {
    "accepted": 2
  }
  ```
</ResponseExample>

## Response Fields

A `202` response body contains a single field confirming how many entries were written.

<ResponseField name="accepted" type="integer">
  The number of log entries persisted from this request. A `202` response with `accepted` equal to the length of the `logs` array confirms that every entry in the batch was written successfully. There is no partial-success shape — a request either succeeds entirely or fails validation entirely, so `accepted` will always match the number of entries you submitted on a `202`.
</ResponseField>

## Response Codes

| Code  | Meaning                                             |
| ----- | --------------------------------------------------- |
| `202` | Accepted — all entries in the batch were persisted. |
| `400` | Validation failed on one or more entries.           |
| `401` | Missing or invalid API key.                         |
| `402` | Monthly ingestion limit reached for your plan.      |
| `409` | Same `Idempotency-Key` is already being processed.  |
| `429` | Rate limited.                                       |

See [Errors](/essentials/errors) for the full response envelope shape and retry guidance.
