Proposal API
Create proposal drafts, update items and sponsor requirements, retrieve links, and follow acceptance and signing through REST API v2 and MCP.
Use the proposal API when another system prepares sponsorship offers for your team. For example, a lead engine can create a draft for an approved sponsor, add the placements and dates you want to offer, then let you review and share it in Sponsy.
Creating a draft does not email the sponsor, publish the offer, or book slots. You choose when to share it. Once the sponsor accepts, Sponsy follows the proposal's normal acceptance settings, including deal and slot creation.
The endpoints are part of REST API v2. V2 beta access and an API v2 bearer token are required. Creating or changing proposals requires Pro or Custom, plus the relevant token permissions. See API setup for access and token management.
A draft-first workflow
- Find or create the customer and contacts you want to address.
- Read the publications and placements to get their IDs and rate settings.
- Create the draft with its items, prices, dates, and sponsor requirements.
- Save the returned proposal ID in your system. Use that ID to read or update the same draft on later runs.
- Review the proposal in Sponsy, then share it with the sponsor.
- Read the proposal again to follow its response and signing state.
Keep sending as a deliberate step. A nightly job that prepares drafts does not need to publish them or send reminders.
Endpoints and permissions
All paths below start at https://api.getsponsy.com/v2. Send Authorization: Bearer <token> with every request. Your token needs the listed proposal permission, and its user must also have access to the workspace and publications involved.
| Method and path | Purpose | Token permission |
|---|---|---|
GET /proposals | List proposals with filters and cursor pagination | proposals.read |
GET /proposals/{id} | Read an offer, its items, link, and lifecycle state | proposals.read |
POST /proposals | Create a draft | proposals.create |
PATCH /proposals/{id} | Edit an offer or archive/unarchive it | proposals.update |
DELETE /proposals/{id} | Soft-delete a draft | proposals.delete |
POST /proposals/{id}/publish | Make a draft available on its offer link | proposals.update |
POST /proposals/{id}/reminders | Explicitly email a proposal reminder | proposals.update |
GET /proposals/{id}/insertion-order | Retrieve a direct proposal's insertion-order PDF | proposals.read |
POST /proposals/{id}/groups/publisher-approval | Ask selected agency groups' publishers for approval | proposals.update |
GET /proposal-groups | List incoming publisher groups | proposals.read |
GET /proposal-groups/{id} | Read an incoming publisher group | proposals.read |
POST /proposal-groups/{id}/accept | Accept as the group's publisher | proposals.update |
POST /proposal-groups/{id}/reject | Reject as the group's publisher, with a reason | proposals.update |
The token user's effective workspace permissions also apply. Prices, unit prices, totals, and agency fees require financials.read. Financial fields are returned as null without it. Financial inputs are ignored without financials.update, preserving existing prices during non-financial edits. Insertion-order PDFs also require financials.read. Requiring a signature needs your workspace's e-signature capability.
Create a draft
Save a request such as this as proposal.json, replacing the example UUIDs with IDs from your workspace:
{
"customerId": "11111111-1111-4111-8111-111111111111",
"dealName": "November newsletter sponsorship",
"extraMessage": "Two November issues for your review.",
"assetsCollectionRequired": true,
"requireSignature": true,
"items": [
{
"publicationId": "22222222-2222-4222-8222-222222222222",
"placementId": "33333333-3333-4333-8333-333333333333",
"priceType": "FLAT",
"price": 750,
"quantity": 2,
"proposedDates": ["2026-11-10", "2026-11-17", "2026-11-24"],
"recommendedDates": ["2026-11-10", "2026-11-17"]
}
]
}curl https://api.getsponsy.com/v2/proposals \
-H "Authorization: Bearer $SPONSY_API_TOKEN" \
-H "Content-Type: application/json" \
--data @proposal.jsonThe response is the created proposal, with its id, status, url, and item IDs. Keep the item IDs when editing those items later. The offer remains a draft even though its future share URL is returned.
Item pricing supports FLAT, CPC, and CPM. Send explicit prices if your job calculates them from rate settings; check the reference for the defaults used when a price is omitted. quantity is the number of placements offered. proposedDates contains the available choices, and recommendedDates marks the choices you recommend. Date values use YYYY-MM-DD; range-based placements also support start|end entries such as 2026-11-01|2026-11-30.
You can also provide main contacts, an associated deal, expiration, payment and asset requirements, deal custom fields, collection selections, and item content or slot-detail fields. Main contacts must belong to the selected customer. Publications, placements, fields, collections, and associated deals are checked against the workspace and the proposal's ownership rules.
To link an offer item to existing items in its associated deal, use the explicit dealItemIds field. Those IDs must belong to that deal and match the item's publication and placement. Internal linkage keys in metadata are reserved; do not put deal or slot IDs into metadata to bypass these checks.
Update, read, and delete
PATCH changes only the fields you include. For example, save this as proposal-update.json to change the message and expiration without changing the items:
{
"extraMessage": "Updated with the dates we discussed.",
"expiresAt": "2026-11-05T17:00:00Z"
}curl -X PATCH "https://api.getsponsy.com/v2/proposals/$PROPOSAL_ID" \
-H "Authorization: Bearer $SPONSY_API_TOKEN" \
-H "Content-Type: application/json" \
--data @proposal-update.jsonOmit items to keep the item list. When you include it, send the complete desired list: retain an existing item's id to edit it, omit id for a new item, and leave an item out to remove it. Sending customFieldValues replaces the proposal's publisher field-value list. Pass null only for fields the reference marks nullable; for example, expiresAt: null clears expiration.
Read the proposal with GET /proposals/{id}. Delete an unwanted draft with DELETE /proposals/{id}. To remove a sent or completed offer from the active list, PATCH {"isArchived": true} instead; PATCH {"isArchived": false} restores it. Completed proposals retain their agreement history and cannot be freely rewritten.
List and follow changes
GET /proposals returns { "data": [...], "cursor": { "beforeCursor": ..., "afterCursor": ... } }. Lists show unarchived proposals by default. Use isArchived=true for archived offers. Filters include status, customerId, associatedDealId, createdAtFrom, createdAtTo, updatedAtFrom, and updatedAtTo.
For a CRM sync, filter by updatedAtFrom and keep the proposal IDs you have already processed. Use a small overlap between polling windows to avoid missing updates at the boundary. Follow the returned afterCursor as the next request's afterCursor, with the same filters and ordering, until it is null. You can set limit, orderBy (createdAt or updatedAt), and orderDirection (ASC or DESC).
Response and signing state
Read the returned status and lifecycle timestamps rather than writing an accepted or signed state into a proposal. Viewing, acceptance, payment, and signing are separate events; a view does not mean the sponsor has agreed to the offer.
The response includes publishedAt, viewedAt, lastViewedAt, viewCount, acceptedAt, rejectedAt, ioSignedAt, and payment timestamps, alongside status and paymentStatus. createdDealId identifies the deal made by acceptance. A safe signature summary is returned when available; signature audit IP addresses and user-agent data are kept private.
Acceptance uses Sponsy's existing proposal workflow. Your integration can read the resulting deal reference and retrieve its bookings through the deal and slot APIs. It does not need to recreate them. The current acceptance flow creates a deal for accepted items; setting createDeal to false does not bypass that existing behavior.
Publish, remind, and download the agreement
POST /proposals/{id}/publish makes the offer available at its link. It does not send an email. You can still review and share the proposal from Sponsy instead. Draft creation and publication do not impersonate sponsor acceptance.
POST /proposals/{id}/reminders is an email action. Send {} for the standard reminder, or provide {"message": "A quick reminder about the November dates."}. It uses the proposal's main contacts, or the customer's primary contacts when none are selected. Do not call this endpoint automatically unless your workflow is meant to send a reminder.
GET /proposals/{id}/insertion-order returns a download URL, filename, and report ID for a direct proposal's PDF, including the signature when present. An accepted proposal's agreement remains downloadable after expiration and when archived. Archived proposals in other states are unavailable; an expired published or in-negotiation offer cannot generate an agreement. Drafts are unavailable too.
Agency proposals
Set isAgencyProposal: true when creating an agency offer. Its items are grouped by publication, with separate groups even when two publications belong to the same publisher. You can use the agency's own publications and its active managed publications, with the applicable contracted fee settings. You cannot use an unrelated workspace's publication by passing its ID.
PATCH can replace agency items and edit group messages, notes, ordering, and archive state through groups. Replacing the item list rebuilds the groups and resets their approvals. Editing only group messages, notes, ordering, or archive state keeps their existing approvals.
Ask for publisher approval with POST /proposals/{id}/groups/publisher-approval and a body such as {"groupIds": ["44444444-4444-4444-8444-444444444444"]}. The publisher can list incoming groups through /proposal-groups and accept or reject its own group. Rejection requires a reason. These publisher actions are separate from the advertiser accepting the proposal.
Agency proposals follow the app's multi-party approval flow. The app does not offer agency signatures or agency insertion-order downloads, so these are not enabled through the API either.
MCP proposal tools
Sponsy MCP exposes the same proposal workflows:
| Tools | Use |
|---|---|
find_proposals, get_proposal | Search and read offers |
create_proposal, update_proposal, delete_proposal | Prepare and manage drafts |
publish_proposal, remind_proposal | Deliberate publication and reminder actions |
get_proposal_insertion_order | Retrieve the direct agreement PDF |
find_proposal_groups, get_proposal_group | Read incoming publisher groups |
send_proposal_groups_for_approval | Request publishers' approval |
accept_proposal_group, reject_proposal_group | Respond as a group's publisher |
Tools with a request payload take those fields in body; record-specific tools take id, and list tools take filters. They use the same workspace, publication, financial, and lifecycle checks as REST. See Sponsy MCP setup to connect a client.
Working safely with drafts
Store the proposal ID against your lead or opportunity so rerunning a job updates the intended record. Before retrying a timed-out create request, check whether the proposal was created. Do not assume every failed connection means nothing was saved.
Treat a proposal link as sensitive. Share it only with the intended sponsor and teammates.
Unknown fields, malformed UUIDs, invalid dates, and invalid item references are rejected. Expect 400 for invalid input or an incompatible proposal state, 401 for missing or invalid authentication, 403 for a permission or capability restriction, and 404 when the requested resource is unavailable to your workspace. A 409 means the proposal or approval changed during the operation; read it again before deciding whether to retry. See the reference for each operation's exact response.
For the full request schemas, response fields, validation rules, and errors, use the generated REST API v2 reference.