---
title: "Pass App Store server notifications to Adapty from your backend"
description: "If your backend must keep its URL in App Store Connect, post Apple's server notifications to Adapty unchanged so that Adapty receives subscription events."
---

> **AI agents**: to search Adapty docs faster and with fewer tokens, install the Adapty skills: `npx adapty skills install --yes`

If your backend must keep its URL in App Store Connect, pass the App Store server notifications from your backend to Adapty unchanged. If your backend doesn't need its URL in App Store Connect, [set up forwarding to your backend in Adapty](enable-app-store-server-notifications#raw-events-forwarding) instead. That route needs no code.

Adapty handles the notifications from your backend as if Apple sent them directly. With [Refund Saver](refund-saver), Adapty still answers the refund requests among them.

If your backend receives real-time developer notifications from Google Play through its own Pub/Sub topic, it needs no code to pass them to Adapty. [Add a push subscription for Adapty to that topic](enable-real-time-developer-notifications-rtdn#send-notifications-to-two-adapty-apps).

To pass App Store server notifications to Adapty:

1. In Adapty, open the app you're connecting: each Adapty app has its own notification URL. Go to [**App settings** > **iOS SDK**](https://app.adapty.io/settings/ios-sdk). In the **App Store server notifications** section, copy the URL from the **URL for App Store server notifications** field.
2. In your backend, post each notification from Apple to the URL you copied. In each request:

   - Keep the body from Apple unchanged: `{"signedPayload": "…"}`.
   - Set the `Content-Type: application/json` header.
   - Don't add an `Authorization` header.

   If Adapty also forwards notifications to your backend, don't post those back to Adapty.

Here are examples for different programming languages:

<Tabs groupId="current-os" queryString>
<TabItem value="python" label="Python" default>

```python showLineNumbers

url = "https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/" # don't forget to replace this URL

payload = '{"signedPayload":"eyJhbGciOiJFUzI1NiIs..."}' # Apple's request body, unchanged

headers = {
  'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload)
```

</TabItem>
<TabItem value="javascript" label="Javascript" default>

```javascript  showLineNumbers
const axios = require('axios');

const url = 'https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/'; // don't forget to replace this URL

const payload = '{"signedPayload":"eyJhbGciOiJFUzI1NiIs..."}'; // Apple's request body, unchanged

const config = {
  method: 'post',
  url: url,
  headers: {
    'Content-Type': 'application/json'
  },
  data: payload,
};

const response = await axios(config);
```

</TabItem>
<TabItem value="php" label="PHP" default>

```php  showLineNumbers
<?php
require_once 'HTTP/Request2.php';

$url = 'https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/'; // don't forget to replace this URL

$payload = '{"signedPayload":"eyJhbGciOiJFUzI1NiIs..."}'; // Apple's request body, unchanged

$request = new HTTP_Request2();
$request->setUrl($url);
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'Content-Type' => 'application/json'
));
$request->setBody($payload);
$response = $request->send();
```

</TabItem>
<TabItem value="ruby" label="Ruby" default>

```ruby  showLineNumbers
require "uri"
require "net/http"

url = URI("https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/") # don't forget to replace this URL

payload = '{"signedPayload":"eyJhbGciOiJFUzI1NiIs..."}' # Apple's request body, unchanged

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/json"
request.body = payload

response = https.request(request)
```

</TabItem>
<TabItem value="java" label="Java" default>

```java showLineNumbers
String url = "https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/"; // don't forget to replace this URL

String payload = "{\"signedPayload\":\"eyJhbGciOiJFUzI1NiIs...\"}"; // Apple's request body, unchanged

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, payload);
Request request = new Request.Builder()
  .url(url)
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .build();
Response response = client.newCall(request).execute();
```

</TabItem>
<TabItem value="go" label="Go" default>

  ```go showLineNumbers
package main

	"fmt"
	"strings"
	"net/http"
)

func main() {
	url := "https://api.adapty.io/api/v1/sdk/apple/webhook/123a258e62fad41bfa734f4b0dbcad45/" // Replace this with the actual URL

	payload := strings.NewReader(`{"signedPayload":"eyJhbGciOiJFUzI1NiIs..."}`) // Apple's request body, unchanged

	method := "POST"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, payload)
	if err != nil {
		fmt.Println("Error creating request:", err)
		return
	}
	req.Header.Add("Content-Type", "application/json")

	res, err := client.Do(req)
	if err != nil {
		fmt.Println("Error sending request:", err)
		return
	}
	defer res.Body.Close()
}
  ```

</TabItem>  </Tabs>

A `200` response doesn't confirm that Adapty could use the notification. Adapty answers before it decodes the signed payload. To confirm that notifications reach Adapty, make a [sandbox purchase](test-purchases-in-sandbox). Then make sure that [the notifications status](enable-app-store-server-notifications#check-that-notifications-reach-adapty) in the **App Store server notifications** section reads **Active**.