SDK quickstart: PHP

The community-maintained openai-php/client library targets the OpenAI shape and works against our endpoint. Compatible with Laravel, Symfony, or vanilla PHP 8.

Install

composer require openai-php/client

Hello world

<?php
require __DIR__ . "/vendor/autoload.php";

$client = OpenAI::factory()
    ->withBaseUri("https://api.greenjoules.cloud/v1")
    ->withApiKey(getenv("JC_API_KEY"))
    ->make();

$response = $client->chat()->create([
    "model" => "auto",
    "messages" => [
        ["role" => "user", "content" => "hi"],
    ],
]);

echo $response->choices[0]->message->content;

Streaming

$stream = $client->chat()->createStreamed([
    "model" => "auto",
    "messages" => [["role" => "user", "content" => "tell a story"]],
]);

foreach ($stream as $chunk) {
    echo $chunk->choices[0]->delta->content ?? "";
}

Laravel integration

// config/services.php
return [
    "joule_cloud" => [
        "key"      => env("JC_API_KEY"),
        "base_uri" => "https://api.greenjoules.cloud/v1",
    ],
];

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(\OpenAI\Client::class, fn () =>
        OpenAI::factory()
            ->withBaseUri(config("services.joule_cloud.base_uri"))
            ->withApiKey(config("services.joule_cloud.key"))
            ->make()
    );
}

// in a controller
public function ask(Request $req, OpenAI\Client $jc)
{
    $r = $jc->chat()->create([
        "model" => "auto",
        "messages" => [["role" => "user", "content" => $req->input("q")]],
    ]);
    return ["answer" => $r->choices[0]->message->content];
}

Joule headers

The community client wraps HTTP under the hood but doesn't fully expose raw response headers. The reliable workaround: pull headers via Guzzle directly for that call, or look up the same data via the receipt API:

$receipt = $jc->receipts()->retrieve($response->id);
echo "joules: " . $receipt->energy->total_joules;

Errors

use OpenAI\Exceptions\ErrorException;

try {
    $client->chat()->create([...]);
} catch (ErrorException $e) {
    if ($e->getCode() === 402) { /* low balance */ }
    if ($e->getCode() === 429) { /* energy budget */ }
}