Usage 

This extension describes the protocol between the fixpunkt social server (server side) and a TYPO3 instance such as fp_social (client side). Both sides use the same classes, so that creation and evaluation are guaranteed to use the same format.

Request flow 

  1. The client side (e.g. fp_social) requests posts from the social server.
  2. The social server gathers the data and creates a response object. Which of the three types is created depends on the result:

  3. The server serializes the object via toArray() and json_encode() and sends the JSON to the client.
  4. The client passes the JSON to the factory SocialServerResponse::fromJson() and receives the matching, typed object back.

Every response carries the fully qualified class name in the type field and the protocol version (currently 2) in the version field. Based on these two fields the factory decides which object to reconstruct and checks version compatibility.

Structure of a post object 

Before we get to the responses, it is worth looking at the JSON of a single Post, since some fields have a particular format here:

Field Format
headline Headline. Empty ("") for many sources (e.g. Facebook).
message HTML text. May contain <br /> and <a> tags as well as emoji placeholders of the form {emoji:9728} (Unicode code point).
update_time Serialized \DateTime object with the keys date, timezone_type and timezone.
hashtags List of strings without a leading # (e.g. "summer").
mentions List of objects with displayName and systemName; empty when there are no mentions.
pictures List of picture URLs.

Example 1: A single post (SocialServerPostResponse) 

Server side – create and output as JSON:

use Fixpunkt\FpSocialBridge\v2\Data\Post;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$post = new Post(
    id: '100000000000001_200000000000001',
    headline: '',
    message: 'Summertime! Here is our favourite recipe for hot days.<br />'
        . "\n" . 'Have fun trying it out {emoji:9728} '
        . '<a href=\'https://social.example.com/hashtag/recipe\'>#recipe</a>',
    post_url: 'https://social.example.com/100000000000001/posts/200000000000001',
    update_time: new \DateTime('2026-06-27 08:00:38+00:00'),
    link: 'https://social.example.com/100000000000001/posts/200000000000001',
    hashtags: ['recipe', 'summer', 'drinks', 'tip'],
    mentions: [],
    pictures: ['https://cdn.example.com/media/image-1.jpg'],
);

$response = new SocialServerPostResponse(SocialServerResponse::version, $post);

echo json_encode($response->toArray());
Copied!

The resulting JSON (values shortened):

{
    "type": "Fixpunkt\\FpSocialBridge\\v2\\Response\\SocialServerPostResponse",
    "version": 2,
    "post": {
        "id": "100000000000001_200000000000001",
        "headline": "",
        "message": "Summertime! ...<br />\n... <a href='...'>#recipe</a>",
        "post_url": "https://social.example.com/100000000000001/posts/200000000000001",
        "update_time": {
            "date": "2026-06-27 08:00:38.000000",
            "timezone_type": 1,
            "timezone": "+00:00"
        },
        "link": "https://social.example.com/100000000000001/posts/200000000000001",
        "hashtags": ["recipe", "summer", "drinks", "tip"],
        "mentions": [],
        "pictures": ["https://cdn.example.com/media/image-1.jpg"]
    }
}
Copied!

Client side – evaluate:

use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$response = SocialServerResponse::fromJson($json);

if ($response instanceof SocialServerPostResponse) {
    $post = $response->getPost();
    echo $post->getMessage();

    foreach ($post->getHashtags() as $hashtag) {
        echo '#' . $hashtag; // the leading # is not part of the value
    }
}
Copied!

Example 2: Multiple posts with pagination (SocialServerPostsResponse) 

This is the most common response: a list of posts plus the cursors for paging. previousPage is empty on the first page; nextPage contains the full URL for the next fetch (or is empty when no further page exists).

Server side – create and output as JSON:

use Fixpunkt\FpSocialBridge\v2\Data\Post;
use Fixpunkt\FpSocialBridge\v2\Data\Posts;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostsResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$posts = new Posts([
    new Post(
        id: '100000000000001_200000000000001',
        headline: '',
        message: 'Summertime! Here is our favourite recipe for hot days.',
        post_url: 'https://social.example.com/100000000000001/posts/200000000000001',
        update_time: new \DateTime('2026-06-27 08:00:38+00:00'),
        link: 'https://social.example.com/100000000000001/posts/200000000000001',
        hashtags: ['recipe', 'summer', 'drinks', 'tip'],
        mentions: [],
        pictures: ['https://cdn.example.com/media/image-1.jpg'],
    ),
    new Post(
        id: '100000000000001_200000000000002',
        headline: '',
        message: 'We will soon present our new project – stay tuned!',
        post_url: 'https://social.example.com/100000000000001/posts/200000000000002',
        update_time: new \DateTime('2026-06-24 17:00:14+00:00'),
        link: 'https://social.example.com/100000000000001/posts/200000000000002',
        hashtags: ['project', 'news', 'outlook'],
        mentions: [],
        pictures: ['https://cdn.example.com/media/image-2.jpg'],
    ),
]);

$response = new SocialServerPostsResponse(
    SocialServerResponse::version,
    $posts,
    previous: '',
    next: 'https://social-server.example.com/networks/example/posts?tx_fpsocialserver_show%5Bafter%5D=QVFI...&tx_fpsocialserver_show%5Bversion%5D=2&cHash=0123456789abcdef0123456789abcdef',
);

echo json_encode($response->toArray());
Copied!

The resulting JSON (posts and nextPage shortened):

{
    "type": "Fixpunkt\\FpSocialBridge\\v2\\Response\\SocialServerPostsResponse",
    "version": 2,
    "posts": [
        {
            "id": "100000000000001_200000000000001",
            "headline": "",
            "message": "Summertime! ...",
            "post_url": "https://social.example.com/100000000000001/posts/200000000000001",
            "update_time": {
                "date": "2026-06-27 08:00:38.000000",
                "timezone_type": 1,
                "timezone": "+00:00"
            },
            "link": "https://social.example.com/100000000000001/posts/200000000000001",
            "hashtags": ["recipe", "summer", "drinks", "tip"],
            "mentions": [],
            "pictures": ["https://cdn.example.com/media/image-1.jpg"]
        },
        {
            "id": "100000000000001_200000000000002",
            "headline": "",
            "message": "We will soon present our new project – stay tuned!",
            "...": "..."
        }
    ],
    "requests": {
        "previousPage": "",
        "nextPage": "https://social-server.example.com/networks/example/posts?tx_fpsocialserver_show%5Bafter%5D=QVFI...&tx_fpsocialserver_show%5Bversion%5D=2&cHash=0123456789abcdef0123456789abcdef"
    }
}
Copied!

Client side – evaluate and iterate over the collection:

use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostsResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$response = SocialServerResponse::fromJson($json);

if ($response instanceof SocialServerPostsResponse) {
    foreach ($response->getPosts() as $post) {
        echo $post->getMessage();
    }

    // Cursors (URLs) for the next and previous page
    $next = $response->getNext();
    $previous = $response->getPrevious();
}
Copied!

Example 3: Error response (SocialServerErrorResponse) 

If an error occurs on the social server, it creates a SocialServerErrorResponse instead of a data response.

Server side – create and output as JSON:

use Fixpunkt\FpSocialBridge\v2\Response\SocialServerErrorResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$response = new SocialServerErrorResponse(
    SocialServerResponse::version,
    code: 42,
    message: 'The requested account does not exist.',
);

echo json_encode($response->toArray());
Copied!

The resulting JSON:

{
    "type": "Fixpunkt\\FpSocialBridge\\v2\\Response\\SocialServerErrorResponse",
    "version": 2,
    "code": 555042,
    "message": "The requested account does not exist."
}
Copied!

Client side – evaluate:

use Fixpunkt\FpSocialBridge\v2\Response\SocialServerErrorResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;

$response = SocialServerResponse::fromJson($json);

if ($response instanceof SocialServerErrorResponse) {
    throw new \RuntimeException(
        $response->getMessage(),
        $response->getCode()
    );
}
Copied!

Handling all types together 

In practice the client side does not know in advance which type will come back. The factory always returns the matching object; instanceof is used to tell them apart:

use Fixpunkt\FpSocialBridge\v2\Response\SocialServerResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostsResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerPostResponse;
use Fixpunkt\FpSocialBridge\v2\Response\SocialServerErrorResponse;

$response = SocialServerResponse::fromJson($json);

if ($response instanceof SocialServerErrorResponse) {
    throw new \RuntimeException(
        $response->getMessage(),
        $response->getCode()
    );
}

if ($response instanceof SocialServerPostsResponse) {
    foreach ($response->getPosts() as $post) {
        echo $post->getMessage();
    }
    $next = $response->getNext(); // URL for the next page
}

if ($response instanceof SocialServerPostResponse) {
    $post = $response->getPost();
}
Copied!