Start here
How Acrobat Sign works
Acrobat Sign has its own vocabulary: transient documents, participant sets, states that aren't statuses, shards. This page explains each idea in plain words and shows where it appears in the SDK, so the rest of the documentation (and Adobe's error messages) make sense.
The big picture
You upload one or more documents. You create an agreement that bundles those documents with a list of participant sets, each with a role (sign, approve, fill in a form…) and a place in the signing order. Adobe emails whoever is first, records every step in an audit trail, moves the agreement through a series of statuses, and when the last person has acted, seals the final PDF. You follow along by polling or through webhooks, then download the signed file.
- DocumentA file to be signed. Uploaded temporarily (transient), kept as a reusable template (library) or fetched from a URL.
- AgreementOne signing transaction: documents + participants + options. Has an id, a status and a history.
- Participant setOne step of the workflow: a role, an order number and one or more members.
- MemberA person (email address) in a participant set. Any one member can act for the whole set.
- StatusWhere the agreement is now, as Adobe reports it:
OUT_FOR_SIGNATURE,SIGNED,CANCELLED… - EventOne entry in the audit trail: created, emailed, viewed, signed, with who, when and from where.
- Data centreThe regional cluster (shard) your account lives on, such as
na4oreu1. Every API call must go there.
Documents: transient, library and URL
Acrobat Sign never signs a file "in place" on your server. It needs its own copy, and there are three
ways to give it one. All three end up as an entry in the agreement's list of files
(fileInfos in the API, FileInfo in the SDK).
| Kind | Lifetime | How you get one | Use it for |
|---|---|---|---|
| Transient document | 7 days | transientDocuments()->upload($file) → FileInfo::transientDocument($id) |
The normal case: a contract generated or chosen for this agreement. |
| Library document | Until deleted | Created in the web app or with libraryDocuments()->create() → FileInfo::libraryDocument($id) |
A template sent many times (a standard NDA), often with form fields placed once in Adobe's editor. |
| URL document | Fetched at creation | FileInfo::url($url, $name) |
A file already on a public HTTPS address. Adobe downloads it itself. |
"Transient" catches people out. The upload is a staging area, not storage: the id stops working after seven days, and you can't list or download transient documents. Once an agreement has been created from it, the agreement holds its own copy, so the seven-day limit no longer matters. Details and edge cases are in Uploading documents.
Agreements
An agreement is the unit of work in Acrobat Sign: "these documents, for these people, in this order".
It has a name (shown in emails and the web app), an id that looks like CBJCHBCAABAA…, a sender,
a status and an event history. Once sent, its documents are fixed: you can change some details (the message,
the expiry, participants in some cases) but not the file being signed. To send a corrected document,
cancel and send a new agreement, or use agreements()->copy() to start a new draft with the
same set-up.
Adobe merges the documents in the order you added them. At the end you can download each one separately or all of them as one combined PDF, with or without the audit report.
Developer accounts rename your agreements
On a free developer account, Adobe prefixes every agreement name with [DEMO USE ONLY]. If your
code compares names (it shouldn't; compare ids or your externalId), the prefix will surprise it.
Participant sets and members
The most important idea in the Acrobat Sign data model is that you don't add people to an agreement; you add participant sets. A set is one step of the workflow. It has:
- a role: what this step does (sign, approve, accept, fill in fields…);
- an order: when this step happens (1, 2, 3…; sets with the same number happen in parallel);
- one or more members: the people who can perform it.
The rule that matters: any one member can complete the set for everyone. A set with two members is "either of these people signs", not "both of these people sign". If you need both signatures, use two sets.
use Conceptlz\AdobeSign\Builder\ParticipantSetInfo;
// Either partner may sign for the firm: ONE signature is collected
ParticipantSetInfo::signer('alice@firm.example', 'bob@firm.example');
// Both must sign, at the same time: TWO signatures are collected
ParticipantSetInfo::signer('alice@firm.example')->order(1);
ParticipantSetInfo::signer('bob@firm.example')->order(1);
The "group" form is useful for shared mailboxes and teams ("anyone in accounts payable can approve"). Every member receives the email; the first to act completes the step and the others are released.
Roles
The role decides what Adobe asks the person to do and which emails they get. The SDK has a
ParticipantRole enum with every role the v6 API accepts, plus shortcuts for the two common ones
(ParticipantSetInfo::signer() and ::approver()). Any other role goes through
ParticipantSetInfo::role().
| Role | What the person does |
|---|---|
SIGNER | Reviews and signs. The only role that produces a signature, and the only one a signature level (SES/AES/QES) can apply to. |
APPROVER | Reviews and approves without signing. Typical for internal sign-off ("legal approves before the CEO signs"). |
ACCEPTOR | Accepts the document, e.g. acknowledging a policy, without a signature field. |
FORM_FILLER | Fills in form fields (prices, dates, addresses) before others sign. |
CERTIFIED_RECIPIENT | Confirms delivery: Adobe records that they received the document. |
DELEGATE_TO_SIGNER, DELEGATE_TO_APPROVER, … | Doesn't act themselves but chooses who will (an assistant routing to the right signatory). |
WITNESS, NOTARY_SIGNER, IN_PERSON_SIGNER, HOST, ELECTRONIC_SEALER | Specialised workflows (witnessed signing, notarisation, in-person signing, organisation seals). They depend on account features. |
Responses can contain two more roles you never send: SENDER and SHARE. They describe
the agreement's owner and people it was shared with. Treat roles (and statuses) as open-ended strings: Adobe adds
values without changing the API version, which is why the SDK's models keep them as plain strings.
CCs and the sender
A CC receives copies of the emails and the final PDF but takes no action; add CCs with
AgreementInfo::cc(). The sender is the Acrobat Sign user who created the agreement.
They own it, can cancel or remind, and receive status emails. The sender doesn't sign unless you also add them as
a participant.
State versus status
The API uses two similar words for two different things, and mixing them up is the most common source of confusion.
- StateWhat you ask for: the stage you want the agreement to be in. You set it when creating (
AgreementInfo::state()) and change it withagreements()->updateState(). Adobe never returns it inGET /agreements/{id}. - StatusWhat Adobe reports: where the agreement actually is. Read it with
agreements()->status($id)orget($id)->status.
status() returns. SIGNED, CANCELLED and EXPIRED are final: nothing moves an agreement out of them.The three creation states
State (AgreementState) | What happens | When to use it |
|---|---|---|
IN_PROCESS | Sent immediately to the first step of the order. The SDK's default. | Almost always. |
AUTHORING | Created but not sent, ready for someone to place signature and form fields in Adobe's editor. | Your documents have no field tags and a person should position fields by hand before sending. |
DRAFT | Saved as an incomplete draft. | You build the agreement over several steps before it is ready. |
Adobe's allowed path is DRAFT → AUTHORING → IN_PROCESS → CANCELLED, changed with
agreements()->updateState($id, 'IN_PROCESS') and friends. Draft and authoring agreements don't
expire: the expiry countdown starts when the agreement is sent.
Statuses you will see
| Status | Meaning |
|---|---|
DOCUMENTS_NOT_YET_PROCESSED | Adobe is still converting and preparing the files. Lasts a few seconds after creation. Some calls (signing links, for example) fail until it passes. |
OUT_FOR_SIGNATURE | Waiting for one or more signers. The usual "in progress" status. |
OUT_FOR_APPROVAL, OUT_FOR_ACCEPTANCE, OUT_FOR_FORM_FILLING, OUT_FOR_DELIVERY | Same idea, when the current step's role isn't a signer. |
SIGNED | Everyone has acted. The final PDF is sealed and ready to download. |
APPROVED, ACCEPTED, DELIVERED, FORM_FILLED | Completed agreements whose participants approved, accepted, received or filled rather than signed. |
CANCELLED | Stopped early: the sender cancelled it, or a participant declined. The events tell you which. |
EXPIRED | The expiry date passed before everyone acted. |
DRAFT, AUTHORING | Not yet sent. |
WAITING_FOR_VERIFICATION, WAITING_FOR_REVISION, PAUSED, … | Less common workflow pauses. The spec lists more, and Adobe may add new ones. |
The SDK's Agreement model adds two shortcuts for the statuses most code cares about:
isSigned() and isOutForSignature(). For anything else compare
$agreement->status as a string, and give unknown values a sensible default.
Each step has a status too
agreements()->members($id) returns every participant set with its own status. That is how you
tell whose turn it is:
| Participant set status | Meaning |
|---|---|
WAITING_FOR_MY_SIGNATURE (or _APPROVAL, _ACCEPTANCE, _FORM_FILLING, …) | It is this step's turn now. |
NOT_YET_VISIBLE | A later step; these people haven't been emailed yet. |
WAITING_FOR_OTHERS | This step is done, and others still have to act. |
COMPLETED | This step is done and the agreement is finished. |
CANCELLED, EXPIRED, REMOVED | The step will never happen. |
Pass includeNextParticipantSet: true to also get nextParticipantSets, the steps whose turn
it is. Adobe leaves that key out unless you ask, even though the spec lists it. The
signing order guide shows both techniques.
The first few seconds
Creating an agreement returns its id immediately, but Adobe processes the documents in the background. For a few
seconds the status is DOCUMENTS_NOT_YET_PROCESSED, some calls answer AGREEMENT_NOT_EXPOSED
or AGREEMENT_NOT_SIGNABLE (both surface as NotFoundException), and participant data can lag
behind the status. Code that creates and then immediately inspects an agreement should poll briefly:
$status = $sign->agreements()->status($agreementId);
for ($i = 0; $status === 'DOCUMENTS_NOT_YET_PROCESSED' && $i < 15; $i++) {
sleep(2);
$status = $sign->agreements()->status($agreementId);
}
If a conversion fails (a corrupt or password-protected file, for example), Adobe cancels the agreement itself and
records an AUTO_CANCELLED_CONVERSION_PROBLEM event.
Events and the audit trail
Acrobat Sign records everything that happens to an agreement as an event: created, emailed
(ACTION_REQUESTED), email opened (EMAIL_VIEWED), documents viewed, identity checked
(EMAIL_OTP_AUTHENTICATED…), signed (ACTION_COMPLETED; DIGSIGNED for a
certificate-based signature), delegated, declined (REJECTED), cancelled (RECALLED), expired.
Each event carries a date, the participant's email, the acting user and, for signatures, details such as the input
type (typed, drawn, image) and the IP address.
The same history is available in three forms:
| Form | SDK | Good for |
|---|---|---|
| Event list (JSON) | agreements()->events($id) | Showing progress in your UI, analytics, checking what happened. |
| Audit report (PDF) | agreements()->auditTrail($id), or combinedDocument($id, attachAuditReport: true) | Evidence: the human-readable record you keep with the contract. |
| Push notifications | webhooks()->create(…) | Reacting as things happen, without polling. |
foreach ($sign->agreements()->events($agreementId)->getEvents() ?? [] as $event) {
printf("%s %-22s %s\n", $event->getDate(), $event->getType(), $event->getParticipantEmail());
}
2026-11-03T14:02:11Z CREATED sender@example.com
2026-11-03T14:02:13Z ACTION_REQUESTED jane@example.com
2026-11-03T14:05:40Z EMAIL_VIEWED jane@example.com
2026-11-03T14:06:02Z ACTION_COMPLETED jane@example.com
Why the audit trail matters legally
An electronic signature is only as strong as the evidence of who made it. For an ordinary (simple) e-signature, the party relying on it may have to prove who signed, and the audit report is that proof: email delivery, access, identity checks, timestamps and IP addresses, sealed by Adobe so later changes are detectable. Keep the audit report with the signed PDF for as long as you keep the contract. Stronger levels (AES, QES) add a certificate issued to the signer; see Signature levels.
Data centres (shards) and base URIs
Acrobat Sign runs in several regional data centres. Adobe calls them shards, and their names appear
in host names: na1, na4, eu1, eu2, jp1,
au1 and so on. An account lives in exactly one, chosen when it was created (an EU customer is usually
on an EU shard, which matters for data residency).
Every account has two addresses on its shard:
| Name in the API | Example | What lives there |
|---|---|---|
apiAccessPoint | https://api.na4.adobesign.com/ | The REST API. The SDK appends api/rest/v6. |
webAccessPoint | https://secure.na4.adobesign.com/ | The web app, and the pages people sign on. Signing links look like https://secure.na4.adobesign.com/public/apiesign?pid=…. |
Calls sent to the wrong shard fail with 403 INVALID_API_ACCESS_POINT. Adobe provides a shard-neutral
host, api.adobesign.com, whose GET /baseUris tells you where an
account lives. The SDK handles all of this:
- Discover once
On the first call it asks
GET /baseUriswith your credentials. - Cache
It stores the answer in the PSR-16 cache you passed (in memory by default) for 24 hours, keyed by a hash of the credential, never the credential itself.
- Recover
If Adobe ever answers
INVALID_API_ACCESS_POINT(the account was moved), it forgets the cached shard, rediscovers it and retries the request once.
OAuth is simpler still: Adobe returns the shard (api_access_point) with the token, so OAuth clients never
need GET /baseUris. US government accounts live on a separate domain (adobesigngov.us); for
those, change ClientOptions::$bootstrapUrl as described in Configuration.
Users: sender, acting user and participant
Three different "who" questions come up, and the API answers each in a different place.
| Who | Meaning | Where you see it |
|---|---|---|
| The API caller | The Acrobat Sign user your credentials act as. An Integration Key acts as the user who created it; an OAuth token acts as the user who authorised your app. | users()->me() |
| The sender | The user who created an agreement. Usually the API caller at the time of creation. | get($id)->senderEmail; senderInfo in members() |
| A participant | Someone in a participant set. They need no Acrobat Sign account. | members($id); participantEmail on events |
| The acting user of an event | Who actually did it, which differs from the participant when someone delegated or a sender acted on a participant's behalf. | actingUserEmail on events |
An account administrator's credentials can act as another user in the same account, so that agreements are
sent from the right person's name and appear in their list. The SDK exposes this as asUser(), which adds
Adobe's x-api-user header:
// Same account, different user: the agreement shows maria@company.example as the sender
$asMaria = $sign->asUser('maria@company.example');
$agreementId = $asMaria->agreements()->create($agreement);
// $sign itself is unchanged and still acts as the key's owner
$sign->agreements()->all();
Users also belong to groups, which carry their own settings (email templates, signing options, which
identity checks are allowed). An agreement belongs to the sender's primary group unless you set another. Many
"why does this work for one user but not another?" questions come down to group settings.
Authentication covers asUser() in depth.
Where next
Common questions
I set the state to IN_PROCESS but the status says OUT_FOR_SIGNATURE. Is that wrong?
No, that is exactly right. IN_PROCESS is the state you asked for ("send it"), and
OUT_FOR_SIGNATURE is the status Adobe reports once it has. States are requests; statuses are facts.
Adobe never returns the state in GET /agreements/{id}.
I put two people in one participant set and only one signature was collected. Why?
A participant set is completed by any one of its members. To collect both signatures, give each person their
own set. Give them the same order() if they may sign at the same time, or different orders if one
must sign first.
Do signers need an Adobe account?
No. Participants sign through a link in the email (or an embedded signing link you give them). Only senders and
API callers need Acrobat Sign accounts. You can require signers to log in to an Adobe account as an identity
check (Verification::adobeAccount()), but that is optional.
Can I change the document after sending?
Not the content being signed: changing it would undermine the signatures already collected. Cancel the agreement
and send a new one. agreements()->copy($id) creates a draft with the same documents and
participants as a starting point. You can update some details in flight, such as the message or expiry; see
Managing agreements.
How do I find out which data centre my account is on?
Look at the web app's address bar after logging in: secure.na4.adobesign.com means na4.
You never need to configure it in the SDK, which discovers and caches it. If you log with a PSR-3 logger, the
request URLs show it too.
How do I tell a declined agreement from one I cancelled?
Both end with status CANCELLED. Read the events: a participant declining is recorded as
REJECTED (with their comment), the sender cancelling as RECALLED. Webhooks report the same
distinction as separate event types.
My transient document expired. Is the agreement I created from it affected?
No. The seven-day limit applies to the upload id only. An agreement keeps its own copy of the file from the moment it is created, for as long as the agreement exists in your account.