# Keap authentication call returns "needsEmptyKey"

**URL:** <https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765>\
**Category:** API Q&A\
**Created:** [July 21, 2025, 6:27pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765 "2025-07-21T18:27:29Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sean\_Manning](https://avatars.discourse-cdn.com/v4/letter/s/e274bd/32.png) [@Sean\_Manning](https://integration.keap.com/u/Sean_Manning)\
**Post date:** [July 21, 2025, 6:27pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/1 "2025-07-21T18:27:29Z")

</div>

I’m doing some development work in a staging version of my company’s website. I believe we’re using the Infusionsoft PHP SDK to connect, although I remain unsure of a lot of things (I didn’t write this code & I’m kind of feeling my way along). I’ve had some trouble with Keap authentication, but at the moment it seems to be working, or at least it isn’t throwing any code errors. However, when I attempt to authenticate to Keap like so:

$infusionsoft = new \Infusionsoft\Infusionsoft(array(  
‘clientId’ =\> ‘[id]’,  
‘clientSecret’ =\> ‘[code]’,  
‘redirectUri’ =\> home\_url(‘/’) . ‘?infusionsoft-callback’,  
));

all I get back is an object with a single property: “needsEmptyKey” = true

What does this mean? How do I fix it?

---

<div class="post-metadata">

**Author:** ![Pav](https://sea1.discourse-cdn.com/flex019/user_avatar/integration.keap.com/pav/32/124_2.png) [@Pav](https://integration.keap.com/u/Pav)\
**Post date:** [July 21, 2025, 7:19pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/2 "2025-07-21T19:19:43Z")

</div>

Taking a look at the SDK it has the following function below using the “needsEmptyKey” property.  
Although the constructor does not return anything.

I am wondering if the SDK has been modified or it is an older version, does the “\_\_construct” function look similar to the below link?

[https://github.com/infusionsoft/infusionsoft-php/blob/master/src/Infusionsoft/Infusionsoft.php](https://github.com/infusionsoft/infusionsoft-php/blob/master/src/Infusionsoft/Infusionsoft.php)

The request function using that property.

```php
 public function request()
    {
        // Before making the request, we can make sure that the token is still
        // valid by doing a check on the end of life.
        $token = $this->getToken();
        if ($this->authenticationType === AuthenticationType::OAuth2AccessToken && $this->isTokenExpired()) {
            throw new TokenExpiredException;
        }

        $params = func_get_args();
        $method = array_shift($params);

        // Some older methods in the API require a key parameter to be sent
        // even if OAuth is being used. This flag can be made false as it
        // will break some newer endpoints.
        if ($this->needsEmptyKey) {
            $params = array_merge(array('key' => 'backwards-compatability'), $params);
        }

        // Reset the empty key flag back to the default for the next request
        $this->needsEmptyKey = true;

        $options = $this->setOptionsForRequest([]);

        $client = $this->getSerializer();

        return $client->request($method, $this->getUrl(), $params, $this->getHttpClient($options));
    }

```

If you want to authorize the connection, then take a look at the sample code here - [infusionsoft-php/samples/taskManager.php at master · infusionsoft/infusionsoft-php · GitHub](https://github.com/infusionsoft/infusionsoft-php/blob/master/samples/taskManager.php)

Right at the bottom of the code is the following line with will allow you to click on the link to authorize the connection.

```php
echo '<a href="' . $infusionsoft->getAuthorizationUrl() . '">Click here to authorize</a>';

```

---

<div class="post-metadata">

**Author:** ![Marion\_Dorsett2](https://sea1.discourse-cdn.com/flex019/user_avatar/integration.keap.com/marion_dorsett2/32/14894_2.png) [@Marion\_Dorsett2](https://integration.keap.com/u/Marion_Dorsett2)\
**Post date:** [July 21, 2025, 7:25pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/3 "2025-07-21T19:25:44Z")

</div>

The connection code you’re showing is for the OAuth connection.

If you’re using a PAT or SAK to connect you can use:

```auto
$infusionsoft = new \Infusionsoft\Infusionsoft(array(
  'apikey' => $PAT_or_SAK_API_KEY,
));

```

However, this SDK has also been deprecated… and an upgrade to the new SDK is recommended:  
[Migration guide from infusionsoft/php-sdk.](https://github.com/infusionsoft/keap-sdk-php/blob/main/MigrationFromInfusionsoftPhpSdk.md)

---

<div class="post-metadata">

**Author:** ![Sean\_Manning](https://avatars.discourse-cdn.com/v4/letter/s/e274bd/32.png) [@Sean\_Manning](https://integration.keap.com/u/Sean_Manning)\
**Post date:** [July 22, 2025, 2:10pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/4 "2025-07-22T14:10:31Z")

</div>

Pav:

The constructor in my version of the Infusionsoft class is simpler than the one at your link: mine is missing lines 108-120.

The request() function you post looks similar to the one my site has in file \_scripts\vendor\infusionsoft\php-sdk\src\Infusionsoft\Infusionsoft.php. However, there are some differences. This is the source code my site is using:

```auto
    public function request()
    {
        // Before making the request, we can make sure that the token is still
        // valid by doing a check on the end of life.
        $token = $this->getToken();
        if ($this->isTokenExpired()) {
            throw new TokenExpiredException;
        }

        $url = $this->url . '?' . http_build_query(array('access_token' => $token->getAccessToken()));

        $params = func_get_args();
        $method = array_shift($params);

        // Some older methods in the API require a key parameter to be sent
        // even if OAuth is being used. This flag can be made false as it
        // will break some newer endpoints.
        if ($this->needsEmptyKey) {
            $params = array_merge(array('key' => $token->getAccessToken()), $params);
        }

        // Reset the empty key flag back to the default for the next request
        $this->needsEmptyKey = true;

        $client = $this->getSerializer();
        $response = $client->request($method, $url, $params, $this->getHttpClient());

        return $response;
    }

```

This looks like an older version of the code you posted. I don’t think this was modified on my end - the developer who wrote this app added his functionality by adding Wordpress hooks and actions. I think it’s just old code. We’re very reluctant to do any upgrades because of the risk of blowing up some piece of our custom-written code. Unfortunately, I don’t have the option of including any kind of user controls: this particular code runs from a number of places and does a couple of things based on whether the current customer has certain Keap tags. So it needs to be invisible to the user (customer). My problem right now is that this needsEmptyKey error is blocking me from querying Keap to see if the customer has those tags.

---

<div class="post-metadata">

**Author:** ![Sean\_Manning](https://avatars.discourse-cdn.com/v4/letter/s/e274bd/32.png) [@Sean\_Manning](https://integration.keap.com/u/Sean_Manning)\
**Post date:** [July 22, 2025, 4:59pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/5 "2025-07-22T16:59:29Z")

</div>

I’ve figured this out. Sort of. I’ve been getting this error while testing some changes in the staging copy of the company website. Staging and production are essentially identical at this time. I tried checking on another and very similar function that I know is working in production, which contains the same code for retrieving tags from Keap. The similar function works in production but fails in the staging copy, in exactly the same way: queries for a contact’s tags return an object with only the “NeedsEmptyKey” property set. I have no idea why this problem would only show up in the staging version, but it seems that’s what is happening.

Still, thank you for your help; I’ve learned a few things as a result of this thread.

---

<div class="post-metadata">

**Author:** ![Pav](https://sea1.discourse-cdn.com/flex019/user_avatar/integration.keap.com/pav/32/124_2.png) [@Pav](https://integration.keap.com/u/Pav)\
**Post date:** [July 22, 2025, 5:26pm UTC](https://integration.keap.com/t/keap-authentication-call-returns-needsemptykey/93765/6 "2025-07-22T17:26:02Z")

</div>

I took a look to see if anyone else had the same issue with the “needsEmptyKey”, but I am only seeing debug entries for it. It is particular to the SDK.

As @Marion_Dorsett2 pointed out, there is a new SDK that has come into operation. You could migrate to that if you want to play it safe.

What version of PHP are you running?

If things are working okay, then that is great.
