---
title: Adopt social content
url: /docs/developers/reference/social-outputs-adopt
---

# Adopt social content

`social.outputs.adopt@1`

Saves a draft post for each account you choose, from a caption you give or the result of an automation. Drafts are never approved, dated or published by this call. It needs a key with **Write social**.

## Save a draft for an account

Replace `YOUR_ACCOUNT_ID` with an account's id.

```curl tabs
# A new operationId for each new request; the same one to retry it.
curl https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations \
  -H "Authorization: Bearer gl_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "target": {
    "featureId": "YOUR_FEATURE_ID",
    "export": "adoptOutputs",
    "contractRef": "social.outputs.adopt@1"
  },
  "operationId": "adopt-post-1",
  "input": {
    "accountRefs": [
      {
        "businessProfileId": "YOUR_BUSINESS_ID",
        "featureId": "juniper-cakes.social",
        "collectionId": "juniper-cakes.accounts",
        "id": "YOUR_ACCOUNT_ID"
      }
    ],
    "caption": "This week: lemon drizzle, fresh from the oven.",
    "mediaRefs": [],
    "format": "post"
  }
}'
```

```javascript tabs
// A new operationId for each new request; the same one to retry it.
const response = await fetch(
  "https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer gl_YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "target": {
        "featureId": "YOUR_FEATURE_ID",
        "export": "adoptOutputs",
        "contractRef": "social.outputs.adopt@1"
      },
      "operationId": "adopt-post-1",
      "input": {
        "accountRefs": [
          {
            "businessProfileId": "YOUR_BUSINESS_ID",
            "featureId": "juniper-cakes.social",
            "collectionId": "juniper-cakes.accounts",
            "id": "YOUR_ACCOUNT_ID"
          }
        ],
        "caption": "This week: lemon drizzle, fresh from the oven.",
        "mediaRefs": [],
        "format": "post"
      }
    }),
  },
);
const receipt = await response.json();
console.log(response.status, receipt);
```

```python tabs
import requests

# A new operationId for each new request; the same one to retry it.
response = requests.post(
    "https://api.growlocal.site/api/v1/profiles/YOUR_BUSINESS_ID/invocations",
    headers={"Authorization": "Bearer gl_YOUR_API_KEY"},
    json={
        "target": {
            "featureId": "YOUR_FEATURE_ID",
            "export": "adoptOutputs",
            "contractRef": "social.outputs.adopt@1",
        },
        "operationId": "adopt-post-1",
        "input": {
            "accountRefs": [
                {
                    "businessProfileId": "YOUR_BUSINESS_ID",
                    "featureId": "juniper-cakes.social",
                    "collectionId": "juniper-cakes.accounts",
                    "id": "YOUR_ACCOUNT_ID",
                },
            ],
            "caption": "This week: lemon drizzle, fresh from the oven.",
            "mediaRefs": [],
            "format": "post",
        },
    },
    timeout=30,
)
print(response.status_code, response.json())
```

## Parameters

What goes in `input`:

| Name | Type | Required | Description |
|---|---|---|---|
| `source` | object | No | Where the content came from, when it is an automation's result. Leave it out for content you wrote. |
| `source.featureId` | string | Yes | The feature the run belongs to. |
| `source.runRef` | string | Yes | The run that made the content. |
| `source.itemKey` | string | Yes | The result's key within that run. |
| `accountRefs` | array of object | Yes | The accounts to write for, 1 to 10, one post version each; one listed twice is refused. |
| `accountRefs[].businessProfileId` | string | Yes | Your business's id. |
| `accountRefs[].featureId` | string | Yes | The `featureId` of the account's row. |
| `accountRefs[].collectionId` | string | Yes | The `collectionId` of the account's row. |
| `accountRefs[].id` | string | Yes | The account's id. |
| `caption` | string | Yes | The caption for each new post version. Refused if longer than an account's platform allows. |
| `mediaRefs` | array of string | Yes | Photos and videos from Drive, in order, by reference; an empty list for none. For an automation's result they must be exactly the media it produced. |
| `format` | one of `post`, `story`, `reel` | Yes | `post`, `story` or `reel`. Stories and reels need media, and an automation's result can only be adopted as a post. |
| `platformOptions` | object | No | Settings for the platform, such as `link` or `privacy`; only those the account's platform takes ([Read post destinations](social-destinations-read.md)). |
| `platformOptions.altText` | string | No | A text description of the picture. |
| `platformOptions.link` | string | No | A link to attach. |
| `platformOptions.firstComment` | string | No | A first comment to add. |
| `platformOptions.title` | string | No | A title, for platforms that have one. |
| `platformOptions.privacy` | one of `public`, `unlisted`, `private` | No | `public`, `unlisted` or `private`. |
| `platformOptions.shareToFeed` | boolean | No | Also share to the feed. |
| `platformOptions.collaborators` | array of string | No | Accounts to credit as collaborators. |
| `platformOptions.allowComments` | boolean | No | Whether people may comment. |
| `platformOptions.allowDuet` | boolean | No | Whether others may make a duet. |
| `platformOptions.allowStitch` | boolean | No | Whether others may stitch it. |
| `platformOptions.discloseBrand` | boolean | No | Marks the post as promoting a brand. |
| `platformOptions.brandedContent` | boolean | No | Marks the post as paid brand content. |
| `platformOptions.aiGenerated` | boolean | No | Marks the post as made with AI. |
| `platformOptions.madeForKids` | boolean | No | Marks the video as made for children. |
| `platformOptions.replySettings` | one of `everyone`, `following`, `mentionedUsers`, `subscribers`, `verified` | No | Who may reply: `everyone`, `following`, `mentionedUsers`, `subscribers` or `verified`. |
| `notes` | string | No | Notes kept with each new post version, never posted. Empty when left out. |
| `contentItemRef` | object | No | Groups the new versions under one content item. |
| `contentItemRef.businessProfileId` | string | Yes | Your business's id. |
| `contentItemRef.featureId` | string | Yes | The `featureId` of the row. |
| `contentItemRef.collectionId` | string | Yes | The `collectionId` of the row. |
| `contentItemRef.id` | string | Yes | The row's id. |

## Response

A call that worked answers `200` with an `applied` receipt; its `output` holds these fields, among others. `GET /catalog/social.outputs.adopt@1` answers the complete shape as JSON Schema.

| Name | Type | Description |
|---|---|---|
| `editions` | array of object | The post versions created, one per account. |
| `editions[].ref` | object | The version's reference. |
| `editions[].revision` | integer | Its revision. |
| `editions[].account` | object | The account it is for. |
| `editions[].account.label` | string | The account's name. |
| `editions[].account.platform` | one of `facebook`, `instagram`, `linkedin`, `x`, `tiktok`, `youtube`, `pinterest`, `threads`, `bluesky` | Its platform. |
| `editions[].account.handle` | string | Its handle on the platform. |
| `editions[].account.connected` | boolean | Whether the account is still connected. |
| `editions[].account.textPost` | object | Whether this platform accepts a text-only post, independently of whether the account is connected. |
| `editions[].account.textPost.compatible` | boolean | True when the connector accepts a post with no media. |
| `editions[].account.textPost.reason` | string | The plain reason when text-only posting is incompatible; omitted otherwise. |
| `editions[].caption` | string | The caption. |
| `editions[].mediaRefs` | array of string | The media, in order. |
| `editions[].format` | one of `post`, `story`, `reel` | `post`, `story` or `reel`. |
| `editions[].notes` | string | The internal notes. |
| `editions[].source` | object or null | Where the content came from, or `null`. |
| `editions[].createdAt` | string | When it was made. |
| `editions[].createdBy` | string or null | Who made it, or `null`. |
| `editions[].approval` | object | Whether the version is approved. A new version is not. |
| `editions[].approval.state` | one of `none`, `approved`, `invalidated` | `none`, `approved` or `invalidated`. |
| `editions[].status` | one of `draft`, `approved`, `scheduled`, `suspended`, `held`, `dispatching`, `accepted`, `published`, `failed`, `uncertain` | Where the version is. A new one is `draft`: it is never scheduled or published here. |
| `editions[].canEdit` | boolean | Whether you may edit it. |
| `editions[].canApprove` | boolean | Whether you may approve it. |
| `editions[].canSchedule` | boolean | Whether you may schedule it. |
| `editions[].canCancel` | boolean | Whether you may cancel it. |
| `editions[].canRemove` | boolean | Whether you may remove it. |

```json title="Response"
{
  "status": "applied",
  "operationId": "adopt-post-1",
  "output": {
    "editions": [
      {
        "ref": {
          "businessProfileId": "YOUR_BUSINESS_ID",
          "featureId": "juniper-cakes.social",
          "collectionId": "juniper-cakes.editions",
          "id": "YOUR_EDITION_ID"
        },
        "revision": 1,
        "account": {
          "ref": {
            "businessProfileId": "YOUR_BUSINESS_ID",
            "featureId": "juniper-cakes.social",
            "collectionId": "juniper-cakes.accounts",
            "id": "YOUR_ACCOUNT_ID"
          },
          "revision": 1,
          "label": "Juniper Cakes",
          "platform": "x",
          "handle": "junipercakes",
          "connected": true,
          "textPost": {
            "compatible": true
          },
          "constraints": null
        },
        "caption": "This week: lemon drizzle, fresh from the oven.",
        "mediaRefs": [],
        "format": "post",
        "platformOptions": {},
        "notes": "",
        "source": null,
        "createdAt": "2026-11-13T09:30:00Z",
        "createdBy": null,
        "contentDigest": "0000000000000000000000000000000000000000000000000000000000000000",
        "approval": {
          "state": "none",
          "digest": null,
          "approvedAt": null,
          "approvedBy": null,
          "review": null
        },
        "publication": null,
        "status": "draft",
        "canEdit": true,
        "canApprove": true,
        "canSchedule": false,
        "canCancel": false,
        "canRemove": true
      }
    ]
  }
}
```

## Errors

Besides the answers every request can get ([Errors](../errors.md)), its receipt can say:

| Status | HTTP | What it means |
|---|---|---|
| `pending` | 202 | Accepted and still running. Look it up with `GET /operations/{operationId}`. |
| `uncertain` | 202 | GrowLocal can't yet tell whether it took effect. Look it up before trying again. |
| `requirements` | 409 | Something must happen first; the receipt lists what. |
| `conflict` | 409 | The row changed since you read it, or this `operationId` was used with a different body. |
| `rejected` | 400, or 403 when it's a matter of access | Not done. `diagnostics` says why: each has a `code`, a `path` into what you sent and a `message`. |

The codes in `diagnostics` you're most likely to see:

| Code | Status | What it means |
|---|---|---|
| `SOCIAL.CAPTION_TOO_LONG` | `rejected` | The caption is longer than an account's platform allows. |
| `SOCIAL.FORMAT_NEEDS_MEDIA` | `rejected` | A story or reel has no media. |
| `SOCIAL.SOURCE_MEDIA_MISMATCH` | `rejected` | With a `source`, `mediaRefs` are not exactly the media the automation produced. |
| `SOCIAL.SOURCE_NOT_FOUND` | `rejected` | No such run for this business. |
| `SOCIAL.SOURCE_NOT_SETTLED` | `rejected` | The run has not finished yet. |
| `SOCIAL.ACCOUNT_DUPLICATE` | `rejected` | An account is listed twice. |
| `SOCIAL.PLATFORM_UNSUPPORTED` | `rejected` | An account's platform cannot be published to. |
