PicPuzzle API

Back to PicPuzzle

Developer API Documentation

Introduction

The PicPuzzle API enables programmatic creation of puzzles. This REST API allows applications to create puzzles with images from a URL, generated via AI based on a text prompt, or provided as a base64-encoded string.

Base URL

https://api.picpuzzle.xyz/api

Authentication

Currently, the API is open for use without authentication. Future versions may require API keys for rate limiting and security.

Endpoints

Create Puzzle

Creates a new puzzle with either an AI-generated image or an image from a URL.

POST/puzzles/create

Content-Type: application/json

Generate AI Image

Generates an image via AI using a text prompt. Supports returning either a CDN URL or a base64-encoded image.

POST/puzzles/generate-image

Content-Type: application/json

Request Parameters
ParameterTypeRequiredDescription
promptstringYesText prompt for AI image generation
response_formatstringNoOptional: "url" (default) or "base64"
Examples
Return URL (default):
// POST https://api.picpuzzle.xyz/api/puzzles/generate-image
{
  "prompt": "A cozy cabin in a snowy forest at twilight"
}
Return Base64:
// POST https://api.picpuzzle.xyz/api/puzzles/generate-image
{
  "prompt": "A cozy cabin in a snowy forest at twilight",
  "response_format": "base64"
}
Response Format
Success Response (200 OK) - URL:
{
  "data": {
    "url": "https://cdn.picpuzzle.xyz/puzzles/1234/generated.jpg"
  }
}
Success Response (200 OK) - Base64:
{
  "data": {
    "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD..."
  }
}
Error Responses:
Missing Prompt (400)
{
  "error": "Prompt is required"
}
Generation Failure (500)
{
  "error": "Failed to generate image",
  "message": "<detailed error message>"
}
Request Parameters
ParameterTypeRequiredDescription
image_typestringYesSource of the image: "ai", "url", or "base64"
promptstringYes (if image_type="ai")Text prompt for AI image generation
image_urlstringYes (if image_type="url")URL of the image to use for the puzzle
image_base64stringYes (if image_type="base64")Base64-encoded image string or Data URI (e.g., data:image/jpeg;base64,...)
difficultystringYesPuzzle difficulty: "easy", "medium", or "hard"
creatorNamestringYesName of the puzzle creator
messagestringNoOptional message to display with the puzzle
secretMessagestringNoOptional message revealed only when puzzle is solved
notify_emailstringNoEmail to notify when the puzzle is attempted or completed
is_publicbooleanNoSet true to make the puzzle publicly visible (also accepts isPublic)
subidstringNoSubscriber/user identifier for tracking and retrieving puzzles
subid2stringNoSecondary subscriber/user identifier for tracking and retrieving puzzles
webhook_urlstringNoOptional webhook URL to receive notifications for puzzle events (attempted, completed, leaderboard_submitted)
Example Requests
AI-generated image:
// POST https://api.picpuzzle.xyz/api/puzzles/create
{
  "image_type": "ai",
  "prompt": "A serene mountain lake with snow-capped peaks at sunset",
  "difficulty": "medium",
  "creatorName": "API Demo",
  "message": "Can you solve this puzzle?",
  "secretMessage": "Congratulations on solving this AI-generated puzzle!",
  "notify_email": "creator@example.com",
  "webhook_url": "https://your-app.com/webhooks/puzzle-events",
  "is_public": true,
  "subid": "user123",
  "subid2": "category_nature"
}
URL image:
// POST https://api.picpuzzle.xyz/api/puzzles/create
{
  "image_type": "url",
  "image_url": "https://example.com/image.jpg",
  "difficulty": "hard",
  "creatorName": "API Demo",
  "message": "This is a challenging puzzle!",
  "secretMessage": "Well done on solving this difficult puzzle!",
  "notify_email": "creator@example.com",
  "webhook_url": "https://your-app.com/webhooks/puzzle-events",
  "is_public": false,
  "subid": "user456",
  "subid2": "category_photos"
}
Base64 image:
// POST https://api.picpuzzle.xyz/api/puzzles/create
{
  "image_type": "base64",
  "image_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...",
  "difficulty": "medium",
  "creatorName": "API Demo",
  "message": "Photo from mobile app selection",
  "secretMessage": "Only visible after solving!",
  "notify_email": "creator@example.com",
  "webhook_url": "https://your-app.com/webhooks/puzzle-events",
  "is_public": true,
  "subid": "user789"
}
Response Format
Success Response (201 Created):
{
  "success": true,
  "message": "Puzzle created successfully",
  "puzzle": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "shareCode": "ABCD1234",
    "shareUrl": "https://picpuzzle.xyz/puzzle/ABCD1234",
    "playUrl": "https://picpuzzle.xyz/puzzle/ABCD1234",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "subid": "user123"
  }
}

Important Response Fields

  • id: Unique puzzle identifier
  • shareCode: Short code for sharing
  • shareUrl: Complete URL that can be shared with users to solve the puzzle
  • playUrl: Complete URL for playing the puzzle (same as shareUrl)
  • accessToken: Token required for deleting the puzzle (store securely)
  • subid: Subscriber/user identifier (if provided in request)
Error Responses
Missing Required Parameters (400)
{
  "error": "Missing required fields",
  "required": ["image_type", "creatorName", "difficulty"]
}
Invalid Image Type (400)
{
  "error": "Invalid image_type",
  "message": "image_type must be 'ai', 'url', or 'base64'"
}
Missing Prompt (400)
{
  "error": "Missing prompt",
  "message": "A prompt is required when image_type is 'ai'"
}
Missing Base64 Image (400)
{
  "error": "Missing image_base64",
  "message": "An image_base64 string is required when image_type is 'base64'"
}
Invalid Difficulty (400)
{
  "error": "Invalid difficulty",
  "validOptions": ["easy", "medium", "hard"]
}

List Public Puzzles

Retrieve a paginated list of puzzles that are publicly visible.

GET/puzzles/public

Query Parameters:

  • limit (optional, default 10): Maximum number of puzzles to return
  • offset (optional, default 0): Number of puzzles to skip for pagination

Each puzzle object now includes a leaderboard array ordered by fastest completion time. The array is truncated to the top 10 attempts by default.

Example:
curl "https://api.picpuzzle.xyz/api/puzzles/public?limit=5&offset=0"
Success Response (200 OK):
{
  "puzzles": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "shareCode": "ABCD1234",
      "creatorName": "API Demo",
      "imageUrl": "https://cdn.picpuzzle.xyz/puzzles/550e8400-e29b-41d4-a716-446655440000/original.jpg",
      "difficulty": "medium",
      "createdAt": "2024-05-01T12:34:56.000Z",
      "playUrl": "https://picpuzzle.xyz/puzzle/ABCD1234",
      "leaderboard": [
        { "player_name": "SpeedSolver", "time_in_seconds": 45, "completed_at": "2024-05-01T14:30:15.000Z" },
        { "player_name": "PuzzleMaster", "time_in_seconds": 67, "completed_at": "2024-05-01T13:45:22.000Z" }
      ]
    }
  ]
}

Retrieve Puzzle

Retrieve puzzles by subscriber ID, share code, or puzzle ID. Note: subid and subid2 may return multiple puzzles, while shareCode and puzzleId return a single puzzle.

GET/puzzles/retrieve

Query Parameters (at least one required):

  • subid (optional): Subscriber/user identifier (returns multiple puzzles)
  • subid2 (optional): Secondary subscriber/user identifier (returns multiple puzzles)
  • shareCode (optional): Puzzle share code (returns single puzzle)
  • puzzleId (optional): Puzzle unique identifier (returns single puzzle)
Examples:
// Retrieve by subscriber ID (returns multiple puzzles)
curl "https://api.picpuzzle.xyz/api/puzzles/retrieve?subid=user123"

// Retrieve by secondary subscriber ID (returns multiple puzzles)
curl "https://api.picpuzzle.xyz/api/puzzles/retrieve?subid2=category_nature"

// Retrieve by share code (returns single puzzle)
curl "https://api.picpuzzle.xyz/api/puzzles/retrieve?shareCode=ABCD1234"

// Retrieve by puzzle ID (returns single puzzle)
curl "https://api.picpuzzle.xyz/api/puzzles/retrieve?puzzleId=550e8400-e29b-41d4-a716-446655440000"
Success Response - Single Puzzle (200 OK):

When retrieving by shareCode or puzzleId: The leaderboard array is truncated to the top 10 attempts by default.

{
  "success": true,
  "puzzle": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "creator_name": "API Demo",
    "message": "Test puzzle message",
    "image_url": "https://cdn.picpuzzle.xyz/puzzles/550e8400-e29b-41d4-a716-446655440000/original.jpg",
    "difficulty": "medium",
    "created_at": "2024-05-01T12:34:56.000Z",
    "tile_count": 16,
    "share_code": "ABCD1234",
    "completed": false,
    "secret_message": "Congratulations!",
    "is_public": true,
    "subid": "user123",
    "subid2": "category_nature",
    "webhookUrl": "https://your-app.com/webhooks/puzzle-events",
    "playUrl": "https://picpuzzle.xyz/puzzle/ABCD1234",
    "leaderboard": [
      { "player_name": "SpeedSolver", "time_in_seconds": 45, "completed_at": "2024-05-01T14:30:15.000Z" },
      { "player_name": "QuickFingers", "time_in_seconds": 89, "completed_at": "2024-05-01T12:20:08.000Z" }
    ]
  }
}
Success Response - Multiple Puzzles (200 OK):

When retrieving by subid or subid2:

{
  "success": true,
  "puzzles": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "creator_name": "API Demo",
      "message": "Test puzzle message",
      "image_url": "https://cdn.picpuzzle.xyz/puzzles/550e8400-e29b-41d4-a716-446655440000/original.jpg",
      "difficulty": "medium",
      "created_at": "2024-05-01T12:34:56.000Z",
      "tile_count": 16,
      "share_code": "ABCD1234",
      "completed": false,
      "secret_message": "Congratulations!",
      "is_public": true,
      "subid": "user123",
      "subid2": "category_nature",
      "webhookUrl": "https://your-app.com/webhooks/puzzle-events",
      "playUrl": "https://picpuzzle.xyz/puzzle/ABCD1234"
    },
    {
      "id": "660f9511-f3ac-52e5-b827-557766551111",
      "creator_name": "API Demo",
      "message": "Another puzzle",
      "image_url": "https://cdn.picpuzzle.xyz/puzzles/660f9511-f3ac-52e5-b827-557766551111/original.jpg",
      "difficulty": "easy",
      "created_at": "2024-05-02T10:15:30.000Z",
      "tile_count": 9,
      "share_code": "EFGH5678",
      "completed": false,
      "secret_message": "Well done!",
      "is_public": false,
      "subid": "user123",
      "subid2": "category_nature",
      "webhookUrl": "https://your-app.com/webhooks/puzzle-events",
      "playUrl": "https://picpuzzle.xyz/puzzle/EFGH5678",
      "leaderboard": []
    }
  ]
}
Error Responses:
Missing Identifier (400)
{
  "success": false,
  "error": "At least one identifier required"
}
Puzzle Not Found (404)
{
  "success": false,
  "error": "Puzzle not found"
}

Update Puzzle

Update specific properties of a puzzle using subid, subid2, shareCode, or puzzleId. Requires the accessToken for verification. Note: subid and subid2 may update multiple puzzles.

PATCH/puzzles/update

Content-Type: application/json

Request Body Parameters (at least one identifier required):

  • subid (optional): Subscriber/user identifier (may update multiple puzzles)
  • subid2 (optional): Secondary subscriber/user identifier (may update multiple puzzles)
  • shareCode (optional): Puzzle share code (updates single puzzle)
  • puzzleId (optional): Puzzle unique identifier (updates single puzzle)
  • accessToken (required): Access token for verification

Updatable properties:

  • If there have been NO attempts: difficulty, creatorName, message, secret_message, notify_email, is_public, subid, subid2
  • If attempts exist: notify_email, is_public, subid, subid2
Examples:
// Update public visibility and notification email by share code
curl -X PATCH "https://api.picpuzzle.xyz/api/puzzles/update" \
  -H "Content-Type: application/json" \
  -d '{
    "shareCode": "ABCD1234",
    "accessToken": "your-access-token-here",
    "is_public": true,
    "notify_email": "creator@example.com"
  }'

// Update difficulty and creatorName (only if no attempts)
curl -X PATCH "https://api.picpuzzle.xyz/api/puzzles/update" \
  -H "Content-Type: application/json" \
  -d '{
    "puzzleId": "550e8400-e29b-41d4-a716-446655440000",
    "accessToken": "your-access-token-here",
    "difficulty": "hard",
    "creatorName": "New Name"
  }'

// Bulk update by subid (may affect multiple puzzles)
curl -X PATCH "https://api.picpuzzle.xyz/api/puzzles/update" \
  -H "Content-Type: application/json" \
  -d '{
    "subid": "user123",
    "accessToken": "your-access-token-here",
    "notify_email": "new-email@example.com",
    "is_public": false
  }'
Success Response (200 OK):
// Single puzzle update
{
  "success": true,
  "message": "Puzzle updated successfully",
  "updatedCount": 1
}

// Multiple puzzle update (by subid or subid2)
{
  "success": true,
  "message": "3 puzzles updated successfully",
  "updatedCount": 3
}
Error Responses:
Missing Identifier (400)
{
  "error": "Missing identifier",
  "message": "At least one identifier (subid, subid2, shareCode, or puzzleId) must be provided"
}
Missing Access Token (400)
{
  "error": "Missing access token",
  "message": "Access token is required for puzzle update"
}
Invalid Access Token (403)
{
  "error": "Forbidden",
  "message": "Invalid access token"
}
Puzzle Not Found (404)
{
  "error": "Puzzle not found",
  "message": "No puzzle matched the provided identifiers"
}
Invalid Difficulty (400)
{
  "error": "Invalid difficulty",
  "message": "Invalid difficulty. Allowed: easy, medium, hard"
}
No Updatable Properties (400)
{
  "error": "No updatable properties provided",
  "message": "Provide one or more allowed properties to update"
}

Delete Puzzle

Delete puzzle(s) using subid, subid2, share code, or puzzle ID. Requires the access token for verification. Note: subid and subid2 may delete multiple puzzles.

DELETE/puzzles/delete

Content-Type: application/json

Request Body Parameters (at least one identifier required):

  • subid (optional): Subscriber/user identifier (may delete multiple puzzles)
  • subid2 (optional): Secondary subscriber/user identifier (may delete multiple puzzles)
  • shareCode (optional): Puzzle share code (deletes single puzzle)
  • puzzleId (optional): Puzzle unique identifier (deletes single puzzle)
  • accessToken (required): Access token for verification
Examples:
// Delete by subscriber ID (may delete multiple puzzles)
curl -X DELETE "https://api.picpuzzle.xyz/api/puzzles/delete" \
  -H "Content-Type: application/json" \
  -d '{
    "subid": "user123",
    "accessToken": "your-access-token-here"
  }'

// Delete by secondary subscriber ID (may delete multiple puzzles)
curl -X DELETE "https://api.picpuzzle.xyz/api/puzzles/delete" \
  -H "Content-Type: application/json" \
  -d '{
    "subid2": "category_nature",
    "accessToken": "your-access-token-here"
  }'

// Delete by share code
curl -X DELETE "https://api.picpuzzle.xyz/api/puzzles/delete" \
  -H "Content-Type: application/json" \
  -d '{
    "shareCode": "ABCD1234",
    "accessToken": "your-access-token-here"
  }'

// Delete by puzzle ID
curl -X DELETE "https://api.picpuzzle.xyz/api/puzzles/delete" \
  -H "Content-Type: application/json" \
  -d '{
    "puzzleId": "550e8400-e29b-41d4-a716-446655440000",
    "accessToken": "your-access-token-here"
  }'
Success Response (200 OK):
// Single puzzle deletion
{
  "success": true,
  "message": "Puzzle deleted successfully",
  "deletedCount": 1
}

// Multiple puzzle deletion (by subid or subid2)
{
  "success": true,
  "message": "3 puzzles deleted successfully",
  "deletedCount": 3
}
Error Responses:
Missing Identifier (400)
{
  "success": false,
  "error": "At least one identifier (subid, subid2, shareCode, or puzzleId) is required"
}
Missing Access Token (400)
{
  "success": false,
  "error": "accessToken is required"
}
Invalid Access Token (403)
{
  "success": false,
  "error": "Invalid access token"
}
Puzzle Not Found (404)
{
  "success": false,
  "error": "Puzzle not found"
}

Get Puzzle Leaderboard

Retrieve the leaderboard for a specific puzzle using either the puzzle ID or share code.

Endpoint:

GET /api/puzzles/{puzzleId}/leaderboard

Example with Puzzle ID:

// GET https://api.picpuzzle.xyz/api/puzzles/123/leaderboard

Example with Share Code:

// GET https://api.picpuzzle.xyz/api/puzzles/ABC123/leaderboard

Response:

{
  "leaderboard": [
    {
      "id": 1,
      "player_name": "Alice",
      "completion_time": 45.2,
      "attempts": 1,
      "completed_at": "2024-01-15T10:30:00Z"
    },
    {
      "id": 2,
      "player_name": "Bob",
      "completion_time": 52.8,
      "attempts": 2,
      "completed_at": "2024-01-15T11:15:00Z"
    }
  ]
}

Code Example

JavaScript (Node.js):

const axios = require('axios');

async function createPuzzle() {
  try {
    const response = await axios.post('https://api.picpuzzle.xyz/api/puzzles/create', {
      image_type: 'url',
      image_url: 'https://picsum.photos/800/600', // Random image for testing
      difficulty: 'medium',
      creatorName: 'API Demo',
      message: 'This puzzle was created via the API!',
      secretMessage: 'Congratulations on solving the API-created puzzle!',
      is_public: true
    });
    
    console.log('Success! Puzzle created:');
    console.log(`Share URL: ${response.data.puzzle.shareUrl}`);
    if (response.data.puzzle.shareCode) {
      console.log(`Share Code: ${response.data.puzzle.shareCode}`);
    }
    return response.data;
  } catch (error) {
    console.error('Error creating puzzle:');
    if (error.response) {
      console.error(`Status: ${error.response.status}`);
      console.error(error.response.data);
      if (error.response.data?.error || error.response.data?.message) {
        console.error(`API error: ${error.response.data.error || error.response.data.message}`);
      }
    } else if (error.request) {
      console.error('No response received from server.');
    } else {
      console.error(error.message);
    }
  }
}

createPuzzle();

Note: Remember to handle errors appropriately in your production code.

AI Image Generation

The PicPuzzle API leverages X.AI's 'grok-2-image' model for generating high-quality images from text prompts. When using the AI option, provide a descriptive prompt for best results.

Tips for Effective Prompts:

  • Be detailed and specific about what you want in the image
  • Describe style, lighting, and mood for more control
  • Include subject, setting, and any important visual elements
  • Keep prompts clear and focused for best results

Webhooks

PicPuzzle supports webhooks to notify your application when specific events occur with your puzzles. When creating a puzzle, you can provide a webhook_url to receive real-time notifications.

Webhook Events

The following events trigger webhook notifications:

  • attempted - Fired when a player makes an attempt on the puzzle
  • completed - Fired when a player successfully completes the puzzle
  • leaderboard_submitted - Fired when a completed attempt is added to the leaderboard

Webhook Payload Format

All webhook payloads are sent as HTTP POST requests with the following structure:

{
  "event": "event_type",
  "puzzle": {
    "id": "puzzle_uuid",
    "shareCode": "ABCD1234",
    "creatorName": "Creator Name",
    "subid": "user123",
    "subid2": "category_nature"
  },
  "data": {
    // Event-specific data (see below)
  },
  "timestamp": "2024-05-01T14:30:15.000Z"
}

Event-Specific Data

attempted:
{
  "data": {
    "playerName": "Player123",
    "timeInSeconds": 45,
    "isCompleted": false
  }
}
completed:
{
  "data": {
    "playerName": "Player123",
    "timeInSeconds": 45,
    "completedAt": "2024-05-01T14:30:15.000Z"
  }
}
leaderboard_submitted:
{
  "data": {
    "playerName": "Player123",
    "timeInSeconds": 45,
    "position": 1,
    "completedAt": "2024-05-01T14:30:15.000Z"
  }
}

Security Considerations

  • Webhook URLs must be HTTPS endpoints
  • Implement proper authentication/validation on your webhook endpoint
  • Consider implementing idempotency to handle duplicate webhook deliveries

Testing Webhooks

You can use services like webhook.site orngrok to test webhook functionality during development.

Rate Limiting & Future Plans

Currently, there are no rate limits enforced on the API. However, please use the API responsibly. Future versions may include rate limiting, authentication, and additional endpoints for puzzle management.