# Push Delivery (/connectors/gcp-pub-sub/how-to/push-delivery)



A subscription with a `push_config` is delivered **push-style**: instead of the client pulling, a
per-subscription connector worker pulls from the queue `gcp.sub.{s}` and **POSTs** each message to
your HTTP(S) endpoint. This guide covers the delivery worker, the envelope shape, the `no_wrapper`
mode, optional OIDC auth, the HTTPS / localhost rule, and the retry → dead-letter behavior.

## Pull ↔ push [#pull--push]

A subscription is either pull or push, and you switch between them at any time:

* `CreateSubscription` with a `push_config`, or `ModifyPushConfig` with one, starts a
  per-subscription **delivery worker**.
* `ModifyPushConfig` with an **empty** config returns the subscription to pull.
* Workers start on `CreateSubscription` / `ModifyPushConfig` (push), stop on switch-to-pull or
  delete, and **drain on connector shutdown**.

See [Subscribing](/connectors/gcp-pub-sub/how-to/subscribing) for the pull paths.

## The wrapped envelope [#the-wrapped-envelope]

By default the worker POSTs a **wrapped JSON envelope**:

```json
{
  "message": {
    "data": "<base64>",
    "attributes": { "key": "value" },
    "messageId": "...",
    "publishTime": "...",
    "orderingKey": "..."
  },
  "subscription": "..."
}
```

* `data` is **base64-encoded** — decode it on receipt.
* `attributes` are the user attributes; the reserved `_pubsub_*` tags are not surfaced here.
* `messageId` / `publishTime` / `orderingKey` mirror the message metadata.

### `no_wrapper` mode [#no_wrapper-mode]

When the subscription's push config sets `no_wrapper`, the worker POSTs the **raw message body**
instead of the envelope, with the attributes surfaced as `x-goog-*` headers when configured. Use
this for endpoints that expect the payload directly.

## Acknowledgement [#acknowledgement]

The HTTP response status is the ack signal:

| Endpoint response   | Effect                                                                                                             |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `2xx`               | The message is **acked**.                                                                                          |
| Non-`2xx` / timeout | **Retried** with backoff.                                                                                          |
| Retry exhaustion    | Republished to the subscription's **dead-letter topic** if one is set, else **dropped** (and a metric increments). |

The retry → dead-letter pipeline shares the same dead-letter machinery as the pull paths — see
[Reliability](/connectors/gcp-pub-sub/how-to/reliability).

## OIDC authentication [#oidc-authentication]

When the push config sets an `oidc_token`, the worker adds an **OIDC JWT** as
`Authorization: Bearer <jwt>`. The token audience defaults to the endpoint URL, letting your
endpoint verify the request originated from the connector. Without `oidc_token`, no `Authorization`
header is sent.

## HTTPS / localhost rule [#https--localhost-rule]

<Callout type="warn">
  **Push endpoints must be HTTPS.** Plain `http://` is allowed **only for localhost** (local
  development). Any non-localhost endpoint must be `https://`. This matches Google's push-endpoint
  requirement and keeps message data off the wire in cleartext. It is the connector's **outbound**
  transport rule and is independent of the inbound emulator mode — which is insecure gRPC by design.
  See [Connectivity & emulator mode](/connectors/gcp-pub-sub/how-to/connectivity-and-emulator-mode).
</Callout>

## Node-local caveat (cluster) [#node-local-caveat-cluster]

<Callout type="warn">
  **Push state is node-local.** The per-subscription worker and its in-flight retries live on the
  node that runs the worker. In a cluster, push subscriptions are part of the node-local family — use
  a sticky load balancer (session affinity) so a subscription's push worker and its retries stay on
  one node. Single-node deployments are unaffected. See
  [Reliability](/connectors/gcp-pub-sub/how-to/reliability).
</Callout>

## Error quick reference [#error-quick-reference]

| Trigger                                | Result                               |
| -------------------------------------- | ------------------------------------ |
| Non-localhost endpoint over plain HTTP | rejected (HTTPS required)            |
| Endpoint returns non-`2xx` / times out | retried with backoff                 |
| Retries exhausted, DLQ set             | republished to the dead-letter topic |
| Retries exhausted, no DLQ              | dropped (+ metric)                   |

## Related [#related]

<Cards>
  <Card title="Subscribing" href="/connectors/gcp-pub-sub/how-to/subscribing" description="The pull paths, leases, ack / nack / extend, and ModifyPushConfig to switch pull ↔ push." />

  <Card title="Reliability" href="/connectors/gcp-pub-sub/how-to/reliability" description="Retry backoff, dead-letter topics, at-least-once, and the node-local delivery state." />

  <Card title="Capabilities" href="/connectors/gcp-pub-sub/reference/capabilities" description="The full RPC surface, including ModifyPushConfig and the push delivery features." />
</Cards>
