Skip to main content

Invite a member

POSThttps://api.fieldtask.example/public/v1/member-invitations
Permissions
members:read, members:invite
Safe retries
Idempotency-Key

Invites one person, by email address, to join your organization as an ordinary member.

The request creates a pending invitation, not a member. After it's saved, FieldTask emails the person a link as separate follow-up work. They become a member only when they open that link, sign in with the invited address and accept. Until then they don't appear in List members and have no member ID.

This endpoint only creates. It never resends, replaces or changes an existing invitation, and it can't invite admins. To retry safely after a lost response, send the same body with the same Idempotency-Key. See Idempotency and safe retries.

Preview

This page is generated from contract 2026-11-03-review.2, a review export that hasn't been released. Invitation creation works only where FieldTask has enabled it.

Before you start​

You need:

  1. API access for your organization. An owner or admin requests it in Settings → API connections. Until FieldTask enables it, every request returns 403 api_access_not_enabled.
  2. A REST credential with the permissions below: an API token created in Settings → API connections, or an access token from an OAuth client-credentials connection. Tokens from an MCP sign-in don't work with the REST API.
  3. An eligible invitation sponsor. The connection invites on behalf of an organization owner or admin, who must still hold that role. See Invitation authority.
  4. A plan that allows inviting members. Solo plans can't invite members, and some plans limit how many invitations an organization can create. Either case returns 422 validation_failed.

Permissions​

PermissionDependency in the permission map
members:readNo dependencies
members:inviteDepends on members:read

No inputs need additional permissions.

To check the outcome later with GET /public/v1/operations/{id}, the connection needs the same two permissions.

Adding a permission to a connection doesn't happen automatically. An owner or admin edits it in Settings → API connections. API tokens keep the same token string when their permissions change. OAuth integrations must also request the new scope when they get an access token.

Request​

Responses​

Created

Response Headers
    Cache-Control

    Responses must not be cached.

    Idempotency-Key

    The operation key used for this create, including a server-generated key when omitted.

    Idempotency-Replayed
    Location
    Operation-Location
    X-Request-ID

    Server-generated request correlation ID.

Invitation authority​

Invitations are always sent on behalf of a person who is allowed to invite: the organization's owner or an active admin. The API calls this person the connection's invitation sponsor.

  • For a REST connection, the sponsor is the owner or admin who created the connection with members:invite, or who most recently saved its permissions in Settings → API connections.
  • The sponsor must stay eligible: their FieldTask account must be active and they must still be the owner or an admin. Their role is checked on every invitation request, not just when the connection was set up.
  • If the sponsor is no longer eligible, every invitation request on the connection (create, list and get) returns 403 operation_forbidden.
  • To fix it, a current owner or admin edits the connection's permissions in Settings → API connections and saves. The person who saves becomes the sponsor.

Each invitation stays tied to the sponsor who was in place when it was created. It can be emailed and accepted only while that person is still the connection's sponsor and still eligible. If the sponsor changes, or loses their role, invitations that are still pending can no longer be accepted; invite those people again once the old invitations have expired or been cancelled.

Through MCP, the sponsor is the owner or admin who approved the AI client's connection. See fieldtask_create_member_invitation.

Field guide​

The request body is a single JSON object. Only email is required.

FieldRequiredWhat to send
emailYesOne email address, up to 256 characters, with no spaces at either end. It's stored exactly as sent.
roleNoOnly member is accepted. Defaults to member. Any other value, such as admin, is rejected.

Anything else in the body is rejected with 422 validation_failed, including fields such as name, projects or organization_id. The organization always comes from your credential.

About email:

  • Comparisons are case-insensitive. Jordan.Example@example.com and jordan.example@example.com are the same recipient.
  • If the address already has a pending invitation in your organization, the request is rejected with 422 validation_failed. This includes invitations created in the FieldTask app. A new key never resends or replaces a pending invitation.
  • FieldTask doesn't check whether the address belongs to an existing FieldTask account or member, and the response never reveals it.
  • The response doesn't say which rule failed. Validation errors never echo the submitted address.

Body and header rules​

  • Send Content-Type: application/json with an uncompressed body. A Content-Encoding header is rejected with 415.
  • The body can be at most 256 KiB and nest at most 10 levels deep.
  • Duplicate property names, NaN and Infinity are rejected with 400 invalid_json.
  • The URL must have no query string and no trailing slash.

What happens after you create an invitation​

StepWhat happensWhat you see
1. CreatedThe invitation is saved with status pending. It expires seven days after created_at.201 with the invitation. expires_at is set.
2. EmailedFieldTask emails the recipient a link, as follow-up work after the request.The operation's integration status. See Email delivery.
3. AcceptedThe recipient signs in with the invited address and accepts. They join as an ordinary member.The invitation's status becomes accepted, and the person appears in List members.
Or: expiredNobody accepted before expires_at. The link stops working.status becomes expired.

A 201 response means the invitation exists. It doesn't mean the email was sent or that the person has joined. To follow the invitation, read it with Get a member invitation using data.id or the Location header.

The acceptance link is never returned by the API. It's only in the email.

Email delivery​

Sending the email is follow-up work reported on the operation, not on the invitation. Read the Operation-Location URL from the 201 response with the same credential:

curl "$FIELDTASK_API_ORIGIN/public/v1/operations/OPERATION_ID" \
--header "Authorization: Bearer $FIELDTASK_API_TOKEN"

data.local_status is always committed: the invitation exists. Right after the create, data.integration.status is pending with one target for the email. That target's status and error_code tell you what happened:

Target statuserror_codeMeaning
pendingnullThe email is queued, being sent, or waiting to be retried after a temporary mail-server problem. FieldTask retries those automatically.
succeedednullThe mail server accepted the message. This isn't proof that it reached the inbox.
failedinvalid_upstream_dataThe mail server refused the message, for example because the address doesn't exist.
failedprovider_unavailableThe mail server couldn't be reached after repeated attempts.
blockedconfiguration_requiredInvitation email isn't available in this environment.
blockedauthorization_requiredWhen it was time to send, the invitation was no longer pending, or the connection or its sponsor was no longer allowed to invite.
outcome_unknownreconciliation_requiredFieldTask couldn't confirm whether the email was sent. It may have been.

Delivery problems never remove the invitation. Once the invitation is accepted, more follow-up work for the new membership can appear on the same operation.

Operation results are kept for seven days, the same as the invitation's lifetime. After that, GET /public/v1/operations/{id} returns 410 operation_result_expired. That doesn't mean the invitation wasn't created.

If the email doesn't arrive​

  1. Read the operation and check the email target.
  2. If it's pending, wait and read it again later.
  3. If it's succeeded or outcome_unknown, ask the recipient to check spam or junk folders, and check the email on the invitation for typos.
  4. If it's failed with invalid_upstream_data, the address is probably wrong. Invite the correct address; that's a different recipient, so it isn't blocked by the first invitation.

Don't create another invitation for the same address straight away. While the first one is pending, a second one is rejected with 422 validation_failed, and the API can't resend. FieldTask also limits how many invitations one address can receive per day. If you need to start over for the same address, have an owner or admin cancel the pending invitation in the FieldTask app, or wait until it has expired, then create a new one.

Accepting an invitation​

Acceptance happens in FieldTask, not through the API. The recipient:

  1. Opens the link in the email before expires_at.
  2. Signs in to FieldTask with an account whose verified email address matches the invitation. Someone without an account creates one and verifies the address first, then returns to the link.
  3. Accepts. They join as an ordinary member, never as an admin.

Each link works once: after the invitation is accepted it can't be used again. Acceptance is refused when:

  • the invitation has expired, been cancelled or already been accepted;
  • the signed-in account's verified addresses don't include the invited address;
  • the person was previously removed from the organization. An API invitation doesn't restore a removed member;
  • the connection that created the invitation has been revoked or has lost members:read or members:invite, or the invitation's sponsor is no longer the connection's sponsor or no longer an owner or admin;
  • the organization's plan doesn't allow another member.

If the invited address belongs to someone who is already an active member, accepting marks the invitation accepted and leaves their membership unchanged.

What the API can't do​

  • Resend an invitation email, or extend an invitation.
  • Cancel or revoke an invitation. Owners and admins do that in the FieldTask app.
  • Accept an invitation on someone's behalf.
  • Invite admins, change a member's role, or remove members.

Idempotency and safe retries​

Networks fail after a request is sent. The Idempotency-Key header lets you retry without creating a second invitation. See Idempotency and recovery for the general rules.

  1. Before sending, generate a new key for the invitation, such as a UUID, and save it with your own record.
  2. Send the request with that key.
  3. If you get no response, a timeout or a 5xx, send the same body with the same key again, with backoff.
SituationResult
New keyThe invitation is created. Response 201, Idempotency-Replayed: false.
Same key, same body, within 7 daysThe original response is returned again. No second invitation is created, no second email is sent, and it isn't counted against your allowances. Response 201, Idempotency-Replayed: true.
Same key, different body409 idempotency_key_reused. Nothing is created.
Same key while the first request is still running409 operation_in_progress with Retry-After. Retry with the same key and body.
Same key more than 7 days later409 idempotency_window_expired. A key can never be reused for a different invitation.

Details that matter:

  • Keys are 16–128 printable ASCII characters.
  • "Same body" means the same JSON value. Property order and whitespace don't matter, but leaving out role is different from sending "role": "member", and a different letter case in email is a different body.
  • A replay returns the original response, so data.status is pending even if the invitation has since been accepted. Read the invitation for its current status.
  • Keys are scoped to the organization, the connection and the kind of request. Sending the same key through a different connection is treated as a new request.
  • Replays are authorized again. If the connection has lost a permission, or its sponsor is no longer eligible, the replay is rejected rather than returned. If the invitation was cancelled in the app, a replay returns 410 resource_gone.
  • Without a key, the server generates one and returns it in the Idempotency-Key response header. That only helps if you receive the response, so always send your own key.

Limits​

Invitations count against the organization's and the connection's daily create allowance, which resets at 00:00 UTC.

Invitations also have their own hourly and daily limits, per organization, per connection and per recipient address. Hourly windows start on the hour and daily windows at 00:00 UTC. FieldTask sets these limits.

When you hit a limit you get 429 with a Retry-After header in seconds: until the window resets for invitation limits, or until 00:00 UTC for the daily create allowance. Retry with the same key. Replaying an already-created invitation with its original key doesn't count again.

Recovering from failures​

What happenedWhat to do
Timeout, connection error or 5xxRetry the same body with the same key, with exponential backoff.
409 operation_in_progressWait for Retry-After seconds and retry with the same key.
429Wait for Retry-After seconds and retry with the same key.
422 validation_failed and you aren't sure whether an earlier attempt workedList invitations newest first and look for the address before trying anything else. A pending invitation for it blocks a new one.
403 operation_forbiddenA current owner or admin re-saves the connection's permissions to become its sponsor, then retry with the same key.
201 receivedStore data.id and the Location and Operation-Location headers. Stop retrying.
Key older than 7 days, or the result expiredDon't create again yet. Look for the invitation with GET /public/v1/member-invitations?ordering=-created_at.

Never switch to a new key just because a response was lost.

Errors​

Errors use application/problem+json. The top-level code is the stable machine-readable value; error adds a readable message, a suggested fix and a documentation link:

{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"code": "validation_failed",
"detail": "The request contains invalid fields.",
"instance": "urn:uuid:99999999-9999-4999-8999-999999999993",
"request_id": "99999999-9999-4999-8999-999999999993",
"error": {
"code": "validation_failed",
"message": "The request contains invalid fields.",
"fix": "Check the documented request fields.",
"docs_url": "https://api.fieldtask.example/public/docs/"
}
}

error.code usually equals code. For a missing permission, code is insufficient_scope and error.code is missing_permission. Validation errors don't say which rule failed, and error responses never echo submitted values.

StatuscodeMeaningWhat to do
400invalid_queryThe URL has a query string. Create requests accept none.Send the request to /public/v1/member-invitations with no query parameters.
400invalid_jsonThe body isn't one valid JSON value, repeats a property name, uses NaN/Infinity or nests deeper than 10 levels.Send a single JSON object encoded as UTF-8.
400invalid_operation_keyIdempotency-Key isn't 16–128 printable ASCII characters.Use a UUID or another 16–128 character key without spaces at either end.
401invalid_tokenThe bearer token is missing, malformed, expired, revoked or issued for a different resource (for example an MCP sign-in).Send a current REST API token or REST OAuth access token as Authorization: Bearer ….
403api_access_not_enabledFieldTask hasn't enabled API access for this organization, or has disabled it.An owner or admin requests access in Settings → API connections.
403insufficient_scopeThe connection lacks members:read or members:invite. error.code is missing_permission.Add the permission named in error.message to the connection, then retry with the same key.
403operation_forbiddenThe connection has no eligible invitation sponsor, its access changed while the request was being processed, or a replay was refused because the connection can no longer read the result.A current owner or admin re-saves the connection's permissions in Settings → API connections to become the sponsor. Then retry with the same key and body.
404resource_not_foundInvitations or invitation creation aren't available in this environment, or the URL is wrong.Check the API origin and path (no trailing slash). Contact FieldTask if you expected invitation creation to be available.
405method_not_allowedInvitation reads are enabled but invitation creation isn't. The Allow header lists the methods that are.Contact FieldTask if you expected invitation creation to be available.
409idempotency_key_reusedThis key was already used with a different body.Resend the original body with this key, or use a new key for a different invitation.
409operation_in_progressA request with the same key is still being processed.Wait for Retry-After seconds, then retry with the same key and body.
409idempotency_window_expiredThis key was used more than seven days ago. It can't be replayed or reused. This doesn't mean the original request failed.Look for the invitation with GET /public/v1/member-invitations?ordering=-created_at before creating anything new.
410resource_goneA replay found that the invitation created with this key no longer exists, for example because it was cancelled in the FieldTask app.Don't create it again automatically. Check with an owner or admin whether it was cancelled on purpose.
413payload_too_largeThe body is larger than 256 KiB.Send only email and, optionally, role.
415unsupported_media_typeContent-Type isn't application/json, or the body is compressed.Send an uncompressed body with Content-Type: application/json.
422validation_failedemail is missing or invalid, role isn't member, the body has another field, the address already has a pending invitation, or the organization's plan doesn't allow more invitations.Check the request against the field guide. If you may have invited this address already, list invitations before retrying. The response doesn't name the rule.
429rate_limit_exceededToo many requests in the current minute, too many concurrent requests, or an hourly or daily invitation limit for the organization, connection or recipient address was reached.Wait for Retry-After seconds, then retry with the same key.
429daily_quota_exceededThe organization or connection has used its daily create allowance. Allowances reset at 00:00 UTC.Wait for Retry-After seconds, or check remaining allowance in Settings → API connections.
503temporarily_unavailableThe service couldn't complete the request. The invitation may or may not have been created.Retry with the same key and body, with exponential backoff.

Every response, including errors, has an X-Request-ID header. Include it, with the time and the status code, when you contact support. Never send tokens or secrets.

Response headers​

A successful create returns 201 Created with these headers:

HeaderAlways presentValue
Cache-ControlYesAlways no-store
Idempotency-KeyYesstring
Idempotency-ReplayedYesstring
LocationYesstring
Operation-LocationYesstring
X-Request-IDYesuuid

The server also returns the Idempotency-Key that was used, including one it generated.