Skip to main content
Solved

API call works from Postman but not from my app

  • August 22, 2024
  • 9 replies
  • 260 views

Forum|alt.badge.img+2

Really weird behaviour here that I can’t get to the bottom of; I have a method which I use to render klaviyo templates, and use this in several projects with no issue. However, in our most recent I’m getting a 400 error on the API call - despite the identical call on Postman working and getting a response.

I’ve verified that the Authorization header, indeed all headers, match the Postman call for my own sanity (they do), and have even just reduced the context down to a single key/value pair to ensure there’s nothing weird going on with content.

However, the 400 issue persists; if I switch on enhanced logging I get the following:

*   Trying 2606:4700:4400::ac40:9377:443...
* Connected to a.klaviyo.com (2606:4700:4400::ac40:9377) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=a.klaviyo.com
*  start date: Jul 22 05:46:38 2024 GMT
*  expire date: Oct 20 05:46:37 2024 GMT
*  subjectAltName: host "a.klaviyo.com" matched cert's "a.klaviyo.com"
*  issuer: C=US; O=Google Trust Services; CN=WE1
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x5568b69836f0)
> post /api/template-render HTTP/2
Host: a.klaviyo.com
authorization: Klaviyo-API-Key pk_xxx
accept: application/json
content-type: application/json
revision: 2024-07-15
content-length: 108

* We are completely uploaded and fine
* old SSL session ID is stale, removing
< HTTP/2 400
< server: cloudflare
< date: Thu, 22 Aug 2024 09:57:51 GMT
< content-type: text/html
< content-length: 155
< cf-ray: -
* The requested URL returned error: 400
* stopped the pause stream!
* Connection #0 to host a.klaviyo.com left intact

For completeness, here are my headers and body:

INFO - 2024-08-22 11:08:12 --> Header: Authorization: Authorization: Klaviyo-API-Key pk_xxx
INFO - 2024-08-22 11:08:12 --> Header: Accept: Accept: application/json
INFO - 2024-08-22 11:08:12 --> Header: Content-Type: Content-Type: application/json
INFO - 2024-08-22 11:08:12 --> Header: revision: revision: 2024-07-15


{
    "data": {
        "type": "template",
        "id": "US2baB",
        "attributes": {
            "context": {
                "salutation": "Hi Test",
                "detail": "TEST"
            }
        }
    }
}

As I mentioned, the method I have is identical in 3 other projects and works no problem there, so I’m a bit stumped as to why this time it’s working in Postman (so not an auth or formatting issue) but not in my project.

Best answer by Verdant Spark

Super weird. I’ve swapped out my method for using the klaviyo SDK and now it’s working. So there’s a header which the API now expects to get (or not) which must have been causing it to fail. So random, and undocumented - but at least it’s working with the SDK instead. Sigh. There goes 6 hours of my life I’m not getting back.

View original
Did this topic or the replies in the thread help you find an answer to your question?

9 replies

Forum|alt.badge.img+31
  • Partner
  • 252 replies
  • August 22, 2024

Hello @Verdant Spark Which Programming language you are using to call the API? Is it possible to share the code to check where is the issue?


Forum|alt.badge.img+2
  • Author
  • Problem Solver I
  • 5 replies
  • August 22, 2024

Hey there

Here’s the method; as I mentioned, I’m not actually questioning this, as it’s from a library we have in 3 other projects where it’s working just fine - there’s something about this particular klaviyo account - not sure if it’s because they’re not paying for the account yet, though that wouldn’t explain why it works from Postman.

The project is a CI4 framework:

/**
     * @param $template
     * @param $first_name
     * @param $passed_data array|null
     * @return mixed
     */
    public function klaviyoRender( $template, $first_name, ?array $passed_data = null ): mixed {
        $salutation = !$first_name || $first_name == '' ? '' : "Hi $first_name";
        $context = new stdClass();
        $context->salutation = $salutation;
        if ( $passed_data ) {
            foreach ( $passed_data as $key => $value ) {
                $context->$key = $value;
            }
        }
        $url = "https://a.klaviyo.com/api/template-render";
        $body = [
            'data' => [
                'type'       => 'template',
                'id'         => $template,
                'attributes' => [
                    'context' => $context
                ]
            ]
        ];
        $client = Services::curlrequest();
        $auth = 'Klaviyo-API-Key ' . getenv( 'KLAVIYO_API_KEY' );
        $client->setHeader( 'Authorization', $auth );
        $client->setHeader( 'Accept', 'application/json' );
        $client->setHeader( 'Content-Type', 'application/json' );
        $client->setHeader( 'revision', '2024-07-15' );
        $client->setBody( json_encode( $body ) );

        try {
            $response = $client->request( 'post', $url, [ 'debug' => WRITEPATH . 'logs/curl_log.txt' ] );
            return json_decode( $response->getBody() )->data->attributes->html;
        }
        catch ( Exception $e ) {
            $this->logError($e);
            return false;
        }
    }

 


Forum|alt.badge.img+31
  • Partner
  • 252 replies
  • August 22, 2024

Hello @Verdant Spark  I don’t see any issue with code.
Did you try echoing the output of json_encode( $body )?


Forum|alt.badge.img+2
  • Author
  • Problem Solver I
  • 5 replies
  • August 22, 2024

Yes - body is set as expected (you can see the body in the initial post)


KeviSunshine
Expert Problem Solver III
Forum|alt.badge.img+8
  • Expert Problem Solver III
  • 159 replies
  • August 22, 2024

Hmm, this is curious…

If this isn’t an encoding issue, I think it must be an API key or ID issue… are you sure that template with that ID exists in this environment? And can you triple-check the API key is the expected one for this klaviyo instance?

Do you get any more data in your error message? Typically a 400-level error will have some response body (or response text) that gives a little bit more information.

For example, my application hit a 409 error this morning and I got the following message returned in the error response.

text: '{"errors":[{"id":"9ab0eb5d-ab41-462d-afab-7168c146c0c3","status":409,"code":"duplicate_profile","title":"Conflict.","detail":"A profile already exists with one of these identifiers.","source":{"pointer":"/data/attributes"},"links":{},"meta":{"duplicate_profile_id":"XXX"}}]}';

Let us know.

 

Cheers,

Kevin.


Forum|alt.badge.img+2
  • Author
  • Problem Solver I
  • 5 replies
  • August 22, 2024

API and template key are good - I’m using same in Postman and get a response :/ The 400 doesn’t seem to come with any meaningful message that I can surface.


Forum|alt.badge.img+2
  • Author
  • Problem Solver I
  • 5 replies
  • August 22, 2024

Just to make sure I’m not batshit crazy I also created a new API key and swapped it out to check. That has the same behaviour.


KeviSunshine
Expert Problem Solver III
Forum|alt.badge.img+8
  • Expert Problem Solver III
  • 159 replies
  • August 22, 2024

Weird!! Have you tried other API calls within this klaviyo instance? Simple calls like fetching a profile?


Forum|alt.badge.img+2
  • Author
  • Problem Solver I
  • 5 replies
  • Answer
  • August 22, 2024

Super weird. I’ve swapped out my method for using the klaviyo SDK and now it’s working. So there’s a header which the API now expects to get (or not) which must have been causing it to fail. So random, and undocumented - but at least it’s working with the SDK instead. Sigh. There goes 6 hours of my life I’m not getting back.