Skip to content

feat: read the project token from POSTHOG_PROJECT_TOKEN - #311

Merged
rafaeelaudibert merged 5 commits into
mainfrom
feat/posthog-token-env
Nov 10, 2026
Merged

rafaeelaudibert merged 5 commits into
mainfrom
feat/posthog-token-env

Conversation

@rafaeelaudibert

@rafaeelaudibert rafaeelaudibert commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

💡 Motivation and Context

PostHog calls the project's public key a project token across its SDKs and docs. The PHP SDK and the Laravel integration name it POSTHOG_API_KEY, so .env files and setup guides for PHP apps use a different name from everything else.

This makes POSTHOG_PROJECT_TOKEN the public name:

Where Before After
PostHog::init() with no token argument reads POSTHOG_API_KEY reads POSTHOG_PROJECT_TOKEN, then POSTHOG_API_KEY
Laravel published config 'api_key' => env('POSTHOG_API_KEY') 'project_token' => env('POSTHOG_PROJECT_TOKEN'), plus the deprecated 'api_key' => env('POSTHOG_API_KEY')
Laravel config read posthog.api_key posthog.project_token, then posthog.api_key
posthog:install and posthog:doctor text, queued delivery error "API key" "project token"

Backwards compatible, so a minor bump for both packages:

  • POSTHOG_API_KEY is still read when POSTHOG_PROJECT_TOKEN is unset or blank.
  • project_token and api_key are separate keys with no cross fallback in the config file, so project_token only ever carries POSTHOG_PROJECT_TOKEN. That keeps existing behavior:
    • Setting or clearing posthog.api_key at runtime still takes effect when POSTHOG_PROJECT_TOKEN is unset.
    • Configs published before this change keep working through their api_key key. They also pick up POSTHOG_PROJECT_TOKEN once the app sets it, because the merged package default supplies project_token.
  • A blank or whitespace POSTHOG_PROJECT_TOKEN (or project_token) falls back to POSTHOG_API_KEY (or api_key).
  • PostHog::ENV_API_KEY stays and is marked @deprecated. The new PostHog::ENV_PROJECT_TOKEN is in the public API snapshot.

Unchanged: the $apiKey parameter names in init() and Client, because renaming them would break callers using named arguments. Internally the Laravel package reads the token through one @internal helper, ProjectToken.

The Laravel README, the Laravel example app, and the init() docblocks use the new name.

test/PostHogTest.php had 23 phpcs warnings on main, which fail the changed-files phpcs job once the file is touched. This PR wraps its long lines, gives its constant a visibility, and disables PSR1.Files.SideEffects for it the way other files in test/ already do.

Divergence from the setup spec

sdk-specs setup asks SDKs to name the option projectToken and keep apiKey as a deprecated alias:

  • init() and new Client() keep their $apiKey parameter. They take the token as their first positional argument, which the spec exempts. PHP named arguments do expose $apiKey, so renaming it would break init(apiKey: …) callers. Adding a second, trailing $projectToken parameter would make the call shape worse for no behavior gain.
  • Laravel api_key config key is deprecated with a comment in the published config and a README note, not a logged warning. A config key has no compile-time deprecation, and warning on every boot for a value the app may not control felt noisy. It's easy to add if we want it.

💚 How did you test it?

Run in Docker (composer:2, PHP 8.5):

Check Result
Core PHPUnit suite 2,695 tests pass. The 2 deprecation notices also appear on main
New core cases POSTHOG_PROJECT_TOKEN alone, POSTHOG_API_KEY alone, both (token wins), blank token falls back
scripts/test-laravel.sh 12 and 13 245 package tests and 30 example tests pass on each
New Laravel cases Same four env cases through the published config, plus whitespace tokens, configs published with only api_key (with and without POSTHOG_PROJECT_TOKEN), runtime api_key overrides and clears, and the example app with only POSTHOG_API_KEY
composer api:check, composer api:check:laravel Core snapshot gains ENV_PROJECT_TOKEN. The Laravel snapshot is unchanged
phpcs on changed core files No warnings

Not run: the PHP 8.2 to 8.4 matrix, which CI covers.

📝 Checklist

  • All commits are signed (see Commit signing)
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm change to generate a change intent file

The intent bumps posthog-php 4.22.1 → 4.23.0 and posthog-laravel 0.3.1 → 0.4.0.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Written with Claude Code (Claude Opus 5.5) while updating PostHog's Laravel install example, which had to override the published config to use the token name.
  • Name choice: POSTHOG_PROJECT_TOKEN, the variable PostHog's setup examples for its other SDKs already use.
  • Rejected: renaming the $apiKey parameters. PHP named arguments make that a breaking change.
  • Rejected after review: a single Laravel token key filled from either variable. It let the environment override runtime and published api_key values, so it's now split into project_token and api_key.
  • Follow-up outside this repo: the Laravel page on posthog.com still shows POSTHOG_API_KEY.

rafaeelaudibert and others added 2 commits November 10, 2026 02:59
The PHP SDK and the Laravel integration read the project token from
POSTHOG_TOKEN, with POSTHOG_API_KEY kept as a fallback. The Laravel config
key becomes `token`, and configs published with `api_key` keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rafaeelaudibert
rafaeelaudibert requested a review from a team as a code owner November 10, 2026 06:02
@rafaeelaudibert rafaeelaudibert self-assigned this Nov 10, 2026
@veria-ai

veria-ai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

PR overview

No security issues were found in this pull request.

Reviews

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 3096d96: 2 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.

Comment thread packages/laravel/src/ProjectToken.php Resolved
Comment thread packages/laravel/config/posthog.php Resolved
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-lib_curl Compliance Report

Date: 2026-11-10T14:50:57.400967+00:00
Duration: 116787ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 31ms
Format Validation.Event Has Uuid ✅ 527ms
Format Validation.Event Has Lib Properties ✅ 527ms
Format Validation.Distinct Id Is String ✅ 527ms
Format Validation.Token Is Present ✅ 527ms
Format Validation.Custom Properties Preserved ✅ 527ms
Format Validation.Event Has Timestamp ✅ 528ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 528ms
Retry Behavior.Retries On 503 ✅ 5836ms
Retry Behavior.Does Not Retry On 400 ✅ 2529ms
Retry Behavior.Does Not Retry On 401 ✅ 2529ms
Retry Behavior.Respects Retry After Header ❌ 6338ms
Retry Behavior.Implements Backoff ✅ 16251ms
Retry Behavior.Retries On 500 ✅ 5636ms
Retry Behavior.Retries On 502 ✅ 5636ms
Retry Behavior.Retries On 504 ✅ 5636ms
Retry Behavior.Max Retries Respected ✅ 17041ms
Deduplication.Generates Unique Uuids ✅ 243ms
Deduplication.Preserves Uuid On Retry ✅ 5635ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10843ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5639ms
Deduplication.No Duplicate Events In Batch ✅ 534ms
Deduplication.Different Events Have Different Uuids ✅ 529ms
Compression.Sends Gzip When Enabled ✅ 527ms
Batch Format.Uses Proper Batch Structure ✅ 529ms
Batch Format.Flush With No Events Sends Nothing ✅ 522ms
Batch Format.Multiple Events Batched Together ✅ 513ms
Error Handling.Does Not Retry On 403 ✅ 2530ms
Error Handling.Does Not Retry On 413 ✅ 2530ms
Error Handling.Retries On 408 ✅ 5634ms

Failures

retry_behavior.respects_retry_after_header

Retry delay too short: 804ms < 2500ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 527ms
Request Payload.Flags Request Uses V2 Query Param ✅ 525ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 525ms
Request Payload.Flags Request Omits Authorization Header ✅ 524ms
Request Payload.Token In Flags Body Matches Init ✅ 526ms
Request Payload.Groups Round Trip ✅ 525ms
Request Payload.Groups Default To Empty Object ✅ 524ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 524ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 524ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 524ms
Request Lifecycle.No Flags Request On Init Alone ✅ 519ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 511ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 529ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 525ms
Retry Behavior.Retries Flags On 502 ✅ 627ms
Retry Behavior.Retries Flags On 504 ✅ 628ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 527ms

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-socket Compliance Report

Date: 2026-11-10T14:51:00.438364+00:00
Duration: 116225ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 30ms
Format Validation.Event Has Uuid ✅ 524ms
Format Validation.Event Has Lib Properties ✅ 526ms
Format Validation.Distinct Id Is String ✅ 526ms
Format Validation.Token Is Present ✅ 526ms
Format Validation.Custom Properties Preserved ✅ 525ms
Format Validation.Event Has Timestamp ✅ 526ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 526ms
Retry Behavior.Retries On 503 ✅ 5836ms
Retry Behavior.Does Not Retry On 400 ✅ 2528ms
Retry Behavior.Does Not Retry On 401 ✅ 2527ms
Retry Behavior.Respects Retry After Header ❌ 6335ms
Retry Behavior.Implements Backoff ✅ 16250ms
Retry Behavior.Retries On 500 ✅ 5635ms
Retry Behavior.Retries On 502 ✅ 5634ms
Retry Behavior.Retries On 504 ✅ 5633ms
Retry Behavior.Max Retries Respected ✅ 16248ms
Deduplication.Generates Unique Uuids ✅ 534ms
Deduplication.Preserves Uuid On Retry ✅ 5642ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10840ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5637ms
Deduplication.No Duplicate Events In Batch ✅ 532ms
Deduplication.Different Events Have Different Uuids ✅ 528ms
Compression.Sends Gzip When Enabled ✅ 526ms
Batch Format.Uses Proper Batch Structure ✅ 525ms
Batch Format.Flush With No Events Sends Nothing ✅ 522ms
Batch Format.Multiple Events Batched Together ✅ 512ms
Error Handling.Does Not Retry On 403 ✅ 2528ms
Error Handling.Does Not Retry On 413 ✅ 2527ms
Error Handling.Retries On 408 ✅ 5633ms

Failures

retry_behavior.respects_retry_after_header

Retry delay too short: 803ms < 2500ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 525ms
Request Payload.Flags Request Uses V2 Query Param ✅ 524ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 523ms
Request Payload.Flags Request Omits Authorization Header ✅ 523ms
Request Payload.Token In Flags Body Matches Init ✅ 523ms
Request Payload.Groups Round Trip ✅ 523ms
Request Payload.Groups Default To Empty Object ✅ 524ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 524ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 523ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 523ms
Request Lifecycle.No Flags Request On Init Alone ✅ 519ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 508ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 527ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 525ms
Retry Behavior.Retries Flags On 502 ✅ 625ms
Retry Behavior.Retries Flags On 504 ✅ 625ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 526ms

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

posthog-php-fork_curl Compliance Report

Date: 2026-11-10T14:50:55.865054+00:00
Duration: 115527ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 26ms
Format Validation.Event Has Uuid ✅ 522ms
Format Validation.Event Has Lib Properties ✅ 524ms
Format Validation.Distinct Id Is String ✅ 522ms
Format Validation.Token Is Present ✅ 522ms
Format Validation.Custom Properties Preserved ✅ 524ms
Format Validation.Event Has Timestamp ✅ 524ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 522ms
Retry Behavior.Retries On 503 ✅ 5842ms
Retry Behavior.Does Not Retry On 400 ✅ 2526ms
Retry Behavior.Does Not Retry On 401 ✅ 2527ms
Retry Behavior.Respects Retry After Header ❌ 6336ms
Retry Behavior.Implements Backoff ✅ 16260ms
Retry Behavior.Retries On 500 ✅ 5635ms
Retry Behavior.Retries On 502 ✅ 5638ms
Retry Behavior.Retries On 504 ✅ 5638ms
Retry Behavior.Max Retries Respected ✅ 16258ms
Deduplication.Generates Unique Uuids ✅ 32ms
Deduplication.Preserves Uuid On Retry ✅ 5635ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10855ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5638ms
Deduplication.No Duplicate Events In Batch ✅ 537ms
Deduplication.Different Events Have Different Uuids ✅ 527ms
Compression.Sends Gzip When Enabled ✅ 529ms
Batch Format.Uses Proper Batch Structure ✅ 526ms
Batch Format.Flush With No Events Sends Nothing ✅ 517ms
Batch Format.Multiple Events Batched Together ✅ 518ms
Error Handling.Does Not Retry On 403 ✅ 2529ms
Error Handling.Does Not Retry On 413 ✅ 2530ms
Error Handling.Retries On 408 ✅ 5638ms

Failures

retry_behavior.respects_retry_after_header

Retry delay too short: 808ms < 2500ms

Feature_Flags Tests

✅ 17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 522ms
Request Payload.Flags Request Uses V2 Query Param ✅ 517ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 518ms
Request Payload.Flags Request Omits Authorization Header ✅ 519ms
Request Payload.Token In Flags Body Matches Init ✅ 519ms
Request Payload.Groups Round Trip ✅ 518ms
Request Payload.Groups Default To Empty Object ✅ 518ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 519ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 518ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 517ms
Request Lifecycle.No Flags Request On Init Alone ✅ 515ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 513ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 520ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 520ms
Retry Behavior.Retries Flags On 502 ✅ 621ms
Retry Behavior.Retries Flags On 504 ✅ 620ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 526ms

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/laravel/README.md Resolved
Matches the variable PostHog suggests for the project token in its other
SDK setups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rafaeelaudibert rafaeelaudibert changed the title feat: read the project token from POSTHOG_TOKEN feat: read the project token from POSTHOG_PROJECT_TOKEN Nov 10, 2026

@marandaneto marandaneto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two reproduced compatibility regressions and a separate configuration naming suggestion.

Comment thread packages/laravel/config/posthog.php Resolved
Comment thread examples/laravel/config/posthog.php Resolved
Comment thread packages/laravel/config/posthog.php Resolved
@marandaneto
marandaneto requested a review from a team November 10, 2026 09:47

@ioannisj ioannisj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 An AI agent wrote this on behalf of @ioannisj.

The two threads already marked blocking reproduce on 169d128: with POSTHOG_API_KEY exported, a runtime config(['posthog.api_key' => ...]) is ignored, and the example app boots disabled with only the old variable set. Past those, the phpcs job is red on test/PostHogTest.php, a config published before this release never reads POSTHOG_PROJECT_TOKEN, and I have one question on the spec's projectToken naming for init().

Comment thread test/PostHogTest.php Resolved
Comment thread lib/PostHog.php Resolved
Comment thread packages/laravel/config/posthog.php Resolved
Comment thread examples/laravel/config/posthog.php Resolved
Comment thread packages/laravel/config/posthog.php Resolved
@ioannisj
ioannisj requested a review from a team November 10, 2026 10:16
The Laravel config now carries `project_token` from POSTHOG_PROJECT_TOKEN and
the deprecated `api_key` from POSTHOG_API_KEY as separate keys. ProjectToken
reads `project_token` first and falls back to `api_key`, so runtime api_key
overrides, legacy published configs and the example app keep working without
the provider guard. Also clears the phpcs warnings in test/PostHogTest.php.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@marandaneto marandaneto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good; no qualifying findings. Verdict: correct.

@marandaneto

Copy link
Copy Markdown
Member

Follow-up: please check the PHP and Laravel code snippets across posthog.com and the PostHog app (including onboarding/setup examples) and update them where needed to use POSTHOG_PROJECT_TOKEN and the Laravel project_token config key. Keep examples aligned with the supported API signatures and note the legacy POSTHOG_API_KEY/api_key fallback where relevant.

@marandaneto

Copy link
Copy Markdown
Member

thanks for the pr @rafaeelaudibert

@gewenyu99

Copy link
Copy Markdown

Ping me when this merges to merge PostHog/context-mill#501

@rafaeelaudibert
rafaeelaudibert merged commit 7003ab4 into main Nov 10, 2026
37 checks passed
@rafaeelaudibert
rafaeelaudibert deleted the feat/posthog-token-env branch November 10, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants