IFTTT accepted our service on the first try. The flow broke on the first redirect. A developer integrating the Publora social posting service with IFTTT found that IFTTT's plain OAuth2 flow, which omits PKCE, was rejected by Publora's authorization server, which requires PKCE. The developer inserted a Cloudflare Worker between the two systems to run the PKCE flow and translate between IFTTT's feed-style trigger format and Publora's API, and documented undocumented requirements including a mandatory test/setup endpoint and silently duplicated ingredient slugs. Last week I was bringing Publora onto IFTTT. Our server was already live, OAuth was working, and the API was there. I expected most of the work to be filling in their application and getting through review. The application passed on the first try, in under a day instead of the estimated one to two weeks. The actual integration broke on the first authorization redirect. Here's what I ran into. The first problem showed up immediately. IFTTT sends plain OAuth2: authorization code, no PKCE. Our authorization server requires PKCE. Their redirect arrives without a code challenge , so our server rejects it. I tried the request in exactly the format IFTTT sends: GET https://mcp.publora.com/authorize ?client id=...&response type=code&scope=ifttt&state=...&redirect uri=... - 302 ...?error=invalid request "expected string, received undefined" on code challenge Add a code challenge to the same request and it reaches the login screen normally. So there wasn't much mystery left: IFTTT doesn't send something our authorization server requires. I could either make PKCE optional on our server for IFTTT or put something between the two. I went with the second option. I added a small Cloudflare Worker between IFTTT and Publora. To IFTTT, it looks like the plain OAuth2 provider it expects: no PKCE and a non-expiring token. On the Publora side, the Worker runs the PKCE flow itself, generating the verifier and challenge before sending the request to our authorization server. That also gave me somewhere to handle the rest of the differences between the two APIs. IFTTT expects endpoints and responses in its own format, while Publora already has its own API. The Worker translates between them. IFTTT treats a trigger as a feed rather than a current state: up to fifty recent events, newest first, each with its own meta.id and meta.timestamp in seconds. For Publora that means one post group may need to become several events, one for each channel. Otherwise the meta.id isn't unique: js for const post of posts { for const target of post.platforms ?? { items.push { content: post.content, account: label connections, target.platformId , network: target.platform, occurred at: at, meta: { id: ${post.postGroupId}:${target.platformId} , timestamp: seconds at }, } ; } } There's another requirement they test separately: every trigger needs to return at least three events during testing, so it behaves like a feed rather than a state sensor. An empty or short response doesn't pass. I also replaced hourly polling with realtime notifications. A Publora webhook is created when the connection is set up, and when an event arrives the Worker calls IFTTT's Realtime API. It doesn't send the event data there; it just tells IFTTT there's something new, and IFTTT fetches the feed itself. This is where I spent most of the time. If you're building an IFTTT service, these are worth knowing before you start running their tests. The test/setup endpoint is required, but I couldn't find it in the docs. Their automated tests hit it second. There is a sample response in the dashboard under View scaffold JSON: it needs a queries section, and media url can't be an empty string. The method isn't specified there, so ours accepts both GET and POST. Ingredient slugs can double without showing it in the UI. The form generates a slug while you're typing the field name, and entering your own can leave you with things like contentcontent and occurred atoccurred at . The resulting slug isn't visible in the form. I only found it in the service export under Tools, Export. A good chunk of my failed tests came from this. The UI didn't show me why tests were failing. I could expand the failed result row, but it was empty. The actual results were available through these endpoints: php POST /services/