Integration recipe
Publish to pixPLace on a schedule with GitHub Actions
A scheduled GitHub Actions workflow uploads an image by URL and publishes a post, with the API key stored in repository secrets.
Who it is for
Developers and teams who already keep content or a release process in a GitHub repository.
What it automates
Publish a post on a schedule or after a release: upload the image by URL, then create the post.
Steps
- In the repository open Settings, Secrets and variables, Actions, and add a secret named PIXPLACE_API_KEY with your key.
- Add the workflow file below as .github/workflows/pixplace.yml. The key is read from secrets.PIXPLACE_API_KEY and never written into the file.
- The first step uploads the image and passes data.publicUrl on as a step output. The second step creates the post.
- The Idempotency-Key uses github.run_id. Re-running a failed run therefore returns the original post instead of a second one.
- When you create the key, tick the `publish` scope. A key without it gets 403 insufficient_scope on these calls.
Example
Placeholders: ppk_live_XXXXXXXX is your key. The ids and the image address are samples. The API address is always https://pixplace.space/api/v1.
.github/workflows/pixplace.yml
name: Publish a post
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Upload the image
id: upload
env:
PIXPLACE_API_KEY: ${{ secrets.PIXPLACE_API_KEY }}
run: |
curl --fail-with-body -sS -X POST https://pixplace.space/api/v1/uploads/from-url \
-H "Authorization: Bearer ${PIXPLACE_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: upload-${{ github.run_id }}" \
-d '{"url": "https://example.com/photos/living-room.jpg"}' > upload.json
echo "url=$(jq -r .data.publicUrl upload.json)" >> "$GITHUB_OUTPUT"
- name: Create the post
env:
PIXPLACE_API_KEY: ${{ secrets.PIXPLACE_API_KEY }}
PUBLIC_URL: ${{ steps.upload.outputs.url }}
run: |
jq -n --arg url "$PUBLIC_URL" '{title: "Minimalist living room", attachments: [{url: $url, type: "IMAGE"}]}' > post.json
curl --fail-with-body -sS -X POST https://pixplace.space/api/v1/posts \
-H "Authorization: Bearer ${PIXPLACE_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: post-${{ github.run_id }}" \
-d @post.jsonCommon errors
| Status | Cause | What to do |
|---|---|---|
| 400 | post.json has an empty title or an empty url, because the upload step failed or its output is empty. | Open the log of the upload step. curl --fail-with-body prints the error body, which names the field. |
| 401 | The API key is missing, malformed, revoked or sent in the wrong header. | The secret is missing or misspelled, so the header is empty. Secrets are not passed to workflows that run from a fork. The header must read Authorization: Bearer followed by your ppk_live_ key. Check the key in Settings → Developer. |
| 403 | The key lacks a scope the call needs: the answer is 403 with error.code insufficient_scope and the missing scope in error.required. | Create a new key in Settings → Developer with the `publish` scope ticked and use it instead; repeating the call with the same key fails the same way. |
| 429 | A per-minute or per-day limit was reached. The Retry-After header and the retryAfter field of the body say how many seconds to wait. | Wait that long and repeat with the SAME Idempotency-Key. The job fails with the error body. Re-run the failed job later: the run id is the same, so the Idempotency-Key is the same. |
| 502 | The server could not download the source URL: it answered 4xx or 5xx, the host does not resolve or the response was compressed. | Open the source URL in a private browser window. It must be public and answer 200 without a login. |
The full list of statuses, the retry rules and the limits are in Errors and retries.