Create plan (Usage-Based only)

Overview

Create a new usage-based plan owned by the authenticated partner and link it via company_plans. The endpoint stores features + review sites from the payload; plan_type is forced usage-based, is_admin_managed=0.

Prerequisites

  • A bearer token. Callable with Partner tokens.

Base URL

EnvironmentURL
Productionhttps://production-api.shoutaboutus.com
Developmenthttps://development-api.shoutaboutus.com

Endpoint

POST /api/v1/partner/plans/create

Authentication

  • Requires a bearer token in the Authorization: Bearer <bearer-token> header.

  • Who can call it: Partner tokens.

  • Usage-based only. Sits in the authenticated group, with no extra route-level guard.

  • createPartnerPlan resolves the partner via getAuthPartner (403 if none). It returns 403 (Partner plans can only be created for usage-based companies.) unless partner.billing_model === 'usage-based'.

  • overage_rate is never accepted — it is server-derived from the partner billing config.

  • The caller must be a main-partner user. Sub-partner users are always rejected with 403 (Sub-partners cannot create, edit, or delete plans — plans are managed by your main partner.).

Validation branches on the partner contract_model. For model-a, hard_stop is required per enabled feature, and service_limit is required only when hard_stop=true (else unlimited + overage). For model-b, hard_stop is forced true at persist (ignored here) and service_limit is optional. per_pull_data is exempt (quota derives from review-site cadence). ≥1 feature must be enabled.

Request body

FieldTypeRequiredNotes
plan_namestringYesmax 100
pricenumberYesmin 0
original_pricenumberNonullable, min 0
statusstringNoactive|inactive (default active)
plan_end_datedateNonullable
short_infostringNonullable, max 255
descriptionstringNonullable
enforcement_modestringNoper-location|aggregate-pool (default per-location)
featuresarrayYesmin 1, ≥1 enabled
features.*.feature_namestringYesai_response, review_flag, response_to_past_reviews, response_posting, review_widgets, review_solicitation_email, review_solicitation_sms, review_qr_code, insight_report, competitive_analysis, per_pull_data, url_finder_review_site, url_finder_landing_page
features.*.enabledbooleanNodefault true
features.*.hard_stopbooleanmodel-a: Yes (per enabled, non-per_pull_data)model-b: ignored
features.*.service_limitintegerconditionalmodel-a: required when hard_stop=true; min 1
features.*.limit_typestringconditionalmonthly|quantity; required if response_to_past_reviews
review_sitesarrayYesmin 1
review_sites.*.review_site_idintegerYesexists in review_sites
review_sites.*.pull_frequencystringYeshourly|daily|weekly|monthly
review_sites.*.pull_intervalintegerNo1–30
review_sites.*.pull_timesarrayNoeach HH:MM
review_sites.*.pull_daysarrayNonullable; each int 0–6

Rate limit

  • No rate limit.

Request body

{
  "plan_name": "ResponseScribe Basic",
  "price": 49.99,
  "original_price": 59.99,
  "status": "active",
  "plan_end_date": "2027-12-31",
  "short_info": "Up to 50 responses/month",
  "description": "Full AI-powered response service.",
  "enforcement_mode": "per-location",
  "features": [
    {
      "feature_name": "ai_response",
      "enabled": true,
      "hard_stop": true,
      "service_limit": 50,
      "limit_type": null
    },
    {
      "feature_name": "response_to_past_reviews",
      "enabled": true,
      "hard_stop": false,
      "limit_type": "monthly"
    },
    {
      "feature_name": "per_pull_data",
      "enabled": true
    }
  ],
  "review_sites": [
    {
      "review_site_id": 1,
      "pull_frequency": "daily",
      "pull_interval": 1,
      "pull_times": [
        "06:00"
      ],
      "pull_days": []
    }
  ]
}

Response

200 — Plan created · 200

{
  "data": {
    "data": {
      "id": 11,
      "plan_name": "ResponseScribe Basic",
      "price": 49.99,
      "original_price": 59.99,
      "status": "active",
      "description": "Full AI-powered response service.",
      "plan_end_date": "2027-12-31T00:00:00.000000Z",
      "short_info": "Up to 50 AI responses/month",
      "plan_type": "usage-based",
      "tier_slug": null,
      "enforcement_mode": "per-location",
      "is_admin_managed": false,
      "created_at": "2026-06-19T11:00:00.000000Z",
      "updated_at": "2026-06-19T11:00:00.000000Z",
      "deleted_at": null,
      "features": [
        {
          "id": 88,
          "plan_id": 11,
          "feature_name": "ai_response",
          "enabled": true,
          "service_limit": 50,
          "limit_type": null,
          "included_units": null,
          "overage_rate": null,
          "hard_stop": true,
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "partner_billing_enabled": true
        },
        {
          "id": 89,
          "plan_id": 11,
          "feature_name": "response_to_past_reviews",
          "enabled": true,
          "service_limit": null,
          "limit_type": "monthly",
          "included_units": 10,
          "overage_rate": "0.0500",
          "hard_stop": false,
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "partner_billing_enabled": true
        },
        {
          "id": 90,
          "plan_id": 11,
          "feature_name": "response_posting",
          "enabled": true,
          "service_limit": null,
          "limit_type": null,
          "included_units": null,
          "overage_rate": "0.0400",
          "hard_stop": false,
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "partner_billing_enabled": true
        },
        {
          "id": 91,
          "plan_id": 11,
          "feature_name": "review_widgets",
          "enabled": true,
          "service_limit": 5,
          "limit_type": null,
          "included_units": null,
          "overage_rate": null,
          "hard_stop": true,
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "partner_billing_enabled": true
        },
        {
          "id": 92,
          "plan_id": 11,
          "feature_name": "per_pull_data",
          "enabled": true,
          "service_limit": null,
          "limit_type": null,
          "included_units": null,
          "overage_rate": null,
          "hard_stop": false,
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "partner_billing_enabled": true
        }
      ],
      "plan_review_sites": [
        {
          "id": 30,
          "plan_id": 11,
          "review_site_id": 44,
          "pull_frequency": "daily",
          "pull_interval": 1,
          "pull_times": [
            "06:00"
          ],
          "pull_days": [],
          "created_at": "2026-06-19T11:00:00.000000Z",
          "updated_at": "2026-06-19T11:00:00.000000Z",
          "deleted_at": null,
          "review_site": {
            "id": 44,
            "name": "Google",
            "link": "http://maps.google.com",
            "review_site_button": "img-google.png",
            "review_site_color": "#3774ee",
            "max_rate": 5,
            "status": 0,
            "show": true,
            "use_sau_scrapper": "Y",
            "publisher": "maps.google.com",
            "auth_type": "oauth",
            "scraping_enabled": true,
            "posting_enabled": true,
            "self_form_enabled": "N",
            "email_on_reply": "N",
            "deleted_at": null,
            "created_at": "2017-01-09T11:33:00.000000Z",
            "updated_at": "2020-05-04T02:44:57.000000Z"
          }
        }
      ]
    }
  }
}

Errors

StatusMeaning
401The bearer token is missing, expired or invalid
403No partner resolves, the partner is not usage-based, or the caller is a sub-partner
422The request failed validation — the response names the fields
500Unexpected server error

Example request

curl --request POST \
  --url "https://production-api.shoutaboutus.com/api/v1/partner/plans/create" \
  --header 'Authorization: Bearer <bearer-token>' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"plan_name": "ResponseScribe Basic", "price": 49.99, "original_price": 59.99, "status": "active", "plan_end_date": "2027-12-31", "short_info": "Up to 50 responses/month", "description": "Full AI-powered response service.", "enforcement_mode": "per-location", "features": [{"feature_name": "ai_response", "enabled": true, "hard_stop": true, "service_limit": 50, "limit_type": null}, {"feature_name": "response_to_past_reviews", "enabled": true, "hard_stop": false, "limit_type": "monthly"}, {"feature_name": "per_pull_data", "enabled": true}], "review_sites": [{"review_site_id": 1, "pull_frequency": "daily", "pull_interval": 1, "pull_times": ["06:00"], "pull_days": []}]}'

Did this page help you?