Go Quick Start

View as Markdown

Go syntax for the five Core Workflows. The canonical page owns ordering and API semantics.

Client

package publora
 
import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "time"
)
 
type Client struct {
    BaseURL string
    APIKey string
    HTTP *http.Client
}
 
func New() *Client {
    return &Client{
        BaseURL: "https://api.publora.com/api/v1",
        APIKey: os.Getenv("PUBLORA_API_KEY"),
        HTTP: &http.Client{Timeout: 30 * time.Second},
    }
}
 
func (c *Client) JSON(method, path string, body any, key string, out any) error {
    payload, err := json.Marshal(body)
    if err != nil { return err }
    req, err := http.NewRequest(method, c.BaseURL+"/"+path, bytes.NewReader(payload))
    if err != nil { return err }
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-publora-key", c.APIKey)
    if key != "" { req.Header.Set("Idempotency-Key", key) }
    resp, err := c.HTTP.Do(req)
    if err != nil { return err }
    defer resp.Body.Close()
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        return fmt.Errorf("Publora returned %s", resp.Status)
    }
    return json.NewDecoder(resp.Body).Decode(out)
}

1. Draft

var draft map[string]any
err := client.JSON("POST", "create-post", map[string]any{
    "content": "Draft from Go",
    "platforms": []string{platformID},
}, "", &draft)

With no scheduledTime, this is intentionally a draft.

2. One-shot schedule

scheduledTime := time.Now().UTC().Add(5 * time.Minute).Format(time.RFC3339)
var scheduled map[string]any
err := client.JSON("POST", "create-post", map[string]any{
    "content": "Scheduled from Go",
    "platforms": []string{platformID},
    "scheduledTime": scheduledTime,
}, requestID, &scheduled)

3. Upload and schedule

Follow the canonical presigned sequence. PUT the file to uploadUrl with a plain http.Request and its media content type; do not add the Publora API key to the storage request. Then:

var completed map[string]any
err = client.JSON("POST", "complete-media/"+mediaFileID, nil, "", &completed)
 
var updated map[string]any
err = client.JSON("PUT", "update-post/"+postGroupID, map[string]any{
    "status": "scheduled",
    "scheduledTime": time.Now().UTC().Add(5 * time.Minute).Format(time.RFC3339),
}, "schedule-"+postGroupID, &updated)

4. Update

err = client.JSON("PUT", "update-post/"+postGroupID, map[string]any{
    "status": "scheduled",
    "scheduledTime": time.Now().UTC().Add(10 * time.Minute).Format(time.RFC3339),
}, "reschedule-"+postGroupID, &updated)

5. Webhook consumer

func webhook(secret []byte) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        raw, err := io.ReadAll(r.Body)
        if err != nil { http.Error(w, "bad body", 400); return }
        mac := hmac.New(sha256.New, secret)
        mac.Write(raw)
        received, err := hex.DecodeString(r.Header.Get("x-publora-signature"))
        if err != nil || !hmac.Equal(received, mac.Sum(nil)) {
            http.Error(w, "invalid signature", 401)
            return
        }
        var envelope map[string]any
        if json.Unmarshal(raw, &envelope) != nil { http.Error(w, "bad json", 400); return }
        w.WriteHeader(http.StatusNoContent)
    }
}

Add crypto/hmac, crypto/sha256, encoding/hex, and io to the imports used by the webhook file. See Webhooks. Batch work and LinkedIn analytics remain discoverable through the OpenAPI document, without duplicating their contract here.