service88 ops

Service API

Service marketplace — bookings, providers, service types, and scheduling.

Queries

QUERY

adminServiceOrder

#

The service job behind an order, for the admin console. Addressed by **order** id rather than request id because that is what the console has: service jobs are listed through adminListOrders, which returns OrderEntity rows, and nothing exposed the way back to the request they were created from. requests.order_id has been written by selectOffer since orders existed and find_by_order_id has been on the repo since the cross-vertical cancellation flow needed it — only the read was missing. Returns null for an order with no service request behind it: an order of another kind, or one whose request was hard-deleted. That is a legitimate answer for a console that lists orders by kind and can be pointed at any id, not an error worth raising. Separate from serviceRequest(id:), which is scoped to the customer who raised the request and the providers bidding on it, and redacts the address for a provider who has not paid to unlock it. An admin is neither of those parties, so that query answers NotFound for them.

Arguments

  • orderId!
Returns
QUERY

adminServiceQuestions

#

Every question on a service, for the console's question builder. Named adminServiceQuestions, not serviceQuestions, because BrowseQuery already owns that name. Two resolvers declaring the same field on a MergedObject do not conflict loudly — one simply wins the merge, and the browse one did, so this query has been unreachable since it was written and the console had no way to read a *disabled* question or an isEnabled flag at all. The difference is not only the flag. BrowseQuestionResponse is the customer's view of a question; this returns the whole row, which is what an editor needs.

Arguments

  • serviceId!
  • includeDisabledBoolean
Returns[!]!
QUERY

availableRequests

#

Open leads I can bid on. serviceId narrows the board to one of my services. Omit it for the union across everything I offer — the provider home feed wants that, and used to fake it with one query per service stitched together on the client, which skewed paging whenever one of them failed.

Arguments

  • serviceId
  • pagination
Returns!
QUERY

browseCategories

#

Browse active service categories.

Arguments

  • parentId
Returns[!]!
QUERY

browseServices

#

Browse active services in a category.

Arguments

  • categoryId!
Returns[!]!
QUERY

creditPackages

#

Packages a provider can buy right now.

Returns[!]!
QUERY

myCreditBalance

#

How many credits I have left.

Returns!
QUERY

myCreditTransactions

#

My credit history, newest first.

Arguments

  • limitInt!

    Default

    20
  • offsetInt!

    Default

    0
Returns[!]!
QUERY

myOfferForRequest

#

My own response to one request — a quote, a call request, or a request for more information — or null if I have not responded. A provider's job screen needs their own offer beside the request: its status picks the primary action, and startedAt is what separates a booked job from one under way. Nothing addressed it by request, so the client paged myOffers looking for a match and stopped after three pages — past which a busy provider's own bid simply disappeared.

Arguments

  • requestId!
Returns
QUERY

myOffers

#

List my submitted offers. Takes pagination and returns page_info like every other list on the platform. It used to take bare limit/offset and return a plain array, which left the client with no total and no way to know whether another page existed.

Arguments

  • status
  • pagination
Returns!
QUERY

myProviderDayCounts

#

Jobs per day for the signed-in provider, for the home week strip. Days with no jobs are absent rather than returned as zero — the client knows which dates it asked for.

Arguments

  • from!
  • to!
Returns[!]!
QUERY

myProviderEarningsChart

#

The signed-in provider's earnings chart.

Arguments

  • timeframe!
Returns!
QUERY

myProviderProfile

#

Get my provider profile.

Returns
QUERY

myProviderServices

#

The services I offer, including any I have paused.

Returns[!]!
QUERY

myServiceRequests

#

List my service requests.

Arguments

  • status
  • pagination
Returns!
QUERY

mySkippedRequests

#

Leads I dismissed with skipRequest, newest dismissal first. availableRequests filters these out, so the Skipped tab had no source at all and could only show what the current session happened to remember. unskipRequest puts one back on the board.

Arguments

  • pagination
Returns!
QUERY

pendingServiceProviders

#

List providers pending approval.

Arguments

  • pagination
Returns!
QUERY

predefinedInfoQuestions

#

Questions a provider can ask about jobs for this service, so the app can render the checklist rather than a blank box.

Arguments

  • serviceId!
Returns[!]!
QUERY

requestInfoRequests

#

Every question asked about one request, oldest first. Readable by the customer who raised the request and by any provider who has responded to it. The provider's ask screen used to keep a session-local list that emptied on every reopen; the customer had no list at all.

Arguments

  • requestId!
Returns[!]!
QUERY

requestOffers

#

List offers for a service request (customer only). Bids on a request, ordered by sort. Defaults to RECOMMENDED, which uses the same scorer Quick Match uses — so the list the customer sees and the choice made on their behalf cannot disagree.

Arguments

  • requestId!
  • sort
Returns[!]!
QUERY

rootServiceCategories

#

List root-level service categories (no parent).

Arguments

  • includeInactiveBoolean
  • pagination
Returns!
QUERY

searchServices

#

Search live services by name or description, across every category. browse_services is category-scoped, so a customer could only find a trade by knowing which category it sits under. A query shorter than two characters returns nothing rather than most of the catalogue.

Arguments

  • queryString!
  • limitInt
Returns[!]!
QUERY

service

#

Get a service by ID.

Arguments

  • id!
Returns
QUERY

serviceCategories

#

List service categories with pagination.

Arguments

  • parentId
  • includeInactiveBoolean
  • pagination
Returns!
QUERY

serviceCategory

#

Get a service category by ID.

Arguments

  • id!
Returns
QUERY

serviceCreditPackages

#

Every package, including retired ones. Admin view.

Returns[!]!
QUERY

serviceCreditSettings

#

This workspace's credit policy. Returns the defaults when none has been configured.

Returns!
QUERY

serviceMarketplaceHealth

#

Service health check query.

ReturnsString!
QUERY

servicePriceAdjustments

#

Every extended-fee request on a job, newest first. Includes the refused and withdrawn ones on purpose: they are the record of what was asked for, and a customer looking at a job they argued about should be able to see the argument. Readable by the job's customer and by the provider who holds it.

Arguments

  • requestId!
Returns[!]!
QUERY

serviceProvider

#

Get a service provider by ID.

Arguments

  • id!
Returns
QUERY

serviceProviderCountNear

#

How many providers offer this service within reach of this location. Separate from the list so the header count does not pay to build and sort rows the screen never renders.

Arguments

  • serviceId!
  • latitudeFloat!
  • longitudeFloat!
ReturnsInt!
QUERY

serviceProviders

#

List service providers with pagination and filters.

Arguments

  • status
  • searchString
  • pagination
Returns!
QUERY

serviceProvidersNear

#

Providers who offer this service and can reach this location, nearest first.

Arguments

  • serviceId!
  • latitudeFloat!
  • longitudeFloat!
  • limitInt
Returns[!]!
QUERY

serviceQuestion

#

Get a service question by ID.

Arguments

  • id!
Returns
QUERY

serviceQuestions

#

Get questions for a service (for request form).

Arguments

  • serviceId!
Returns[!]!
QUERY

serviceRequest

#

One service request, by id. Readable by the customer who raised it and by a provider who has responded to it or can still bid on it; the address stays redacted for a provider who has neither unlocked the lead nor won the job. Both apps needed this and neither had it: the customer's detail screen was paging through myServiceRequests looking for one id, and the provider's job detail could not be rendered at all once a job left the open lead board.

Arguments

  • id!
Returns!
QUERY

servicesByCategory

#

List services by category with pagination.

Arguments

  • categoryId!
  • includeInactiveBoolean
  • pagination
Returns!
QUERY

serviceStats

#

Headline numbers for a service's detail screen.

Arguments

  • serviceId!
Returns!

Mutations

MUTATION

approveServiceProvider

#

Approve a service provider (set status to Active).

Arguments

  • input!
Returns!
MUTATION

askForInformation

#

Ask the customer for what you need before quoting.

Arguments

  • input!
Returns[!]!
MUTATION

cancelServiceFee

#

Take back a fee request the customer has not answered yet.

Arguments

  • adjustmentId!
Returns!
MUTATION

cancelServiceRequest

#

Cancel a service request.

Arguments

  • requestId!
Returns!
MUTATION

confirmCompletion

#

Customer confirms completion.

Arguments

  • requestId!
Returns!
MUTATION

confirmServiceCreditPurchase

#

Finish a purchase that needed 3DS. Safe to call more than once.

Arguments

  • purchaseId!
Returns!
MUTATION

confirmServicePayment3ds

#

Finish a card payment the customer had to authenticate (3DS). payServiceRequest hands back a client secret when the bank wants a challenge and leaves the job awaiting payment. This is what the client calls once the challenge is done — without it the customer authenticated and the booking stayed unpaid for ever. Safe to call twice: a payment already captured returns the job as it stands rather than charging again.

Arguments

  • input!
Returns!
MUTATION

createPushProvider

#

Create a push provider (FCM). Platform-owned providers require a platform admin; tenant-owned providers are scoped to the caller's tenant.

Arguments

  • input!
Returns!
MUTATION

createService

#

Create a new service.

Arguments

  • input!
Returns!
MUTATION

createServiceCategory

#

Create a new service category.

Arguments

  • input!
Returns!
MUTATION

createServiceCreditPackage

#

Arguments

  • input!
Returns!
MUTATION

createServiceQuestion

#

Create a new service question.

Arguments

  • input!
Returns!
MUTATION

createServiceRequest

#

Create a new service request.

Arguments

  • input!
Returns!
MUTATION

decideServiceFee

#

The customer's answer. Approving is the only thing that moves a job's total after the customer has been shown it, so it is deliberately a separate call from reading the request — and it is idempotent: the second answer is refused rather than applied.

Arguments

  • adjustmentId!
  • approvedBoolean!
Returns!
MUTATION

deletePushProvider

#

Delete a push provider.

Arguments

  • idString!
ReturnsBoolean!
MUTATION

deleteService

#

Delete a service (soft delete).

Arguments

  • id!
ReturnsBoolean!
MUTATION

deleteServiceCategory

#

Delete a service category (soft delete).

Arguments

  • id!
ReturnsBoolean!
MUTATION

deleteServiceCreditPackage

#

Retire a package. Past purchases keep referencing it.

Arguments

  • id!
ReturnsBoolean!
MUTATION

deleteServiceQuestion

#

Delete a service question.

Arguments

  • id!
ReturnsBoolean!
MUTATION

generateServiceQuestionTemplate

#

Ask the tenant's default LLM for a starter set of intake questions for a new service category. The admin reviews, edits, and accepts; this mutation does NOT persist the questions. Admin-only. Requires the tenant to have serviceQuestionGenEnabled turned on under Settings → AI Features and at least one configured AI provider with a remaining monthly budget.

Arguments

  • input!
Returns!
MUTATION

markServiceComplete

#

Provider marks a service as complete.

Arguments

  • requestId!
  • completionNotesString
Returns!
MUTATION

payServiceRequest

#

Pay for an accepted job.

Arguments

  • input!
Returns!
MUTATION

proposeServiceFee

#

Ask the customer to agree to an extended fee on a job in progress. One at a time per job: a second request while the first is unanswered is refused, because the customer would otherwise be agreeing to one of two live figures.

Arguments

  • input!
Returns!
MUTATION

purchaseServiceCreditPackage

#

Buy a credit package.

Arguments

  • input!
Returns!
MUTATION

raiseDispute

#

Customer raises a dispute.

Arguments

  • requestId!
  • reasonString!
Returns!
MUTATION

recordServiceCashPayment

#

Provider confirms cash changed hands. Cash-only — card and wallet settle through the PSP.

Arguments

  • requestId!
Returns!
MUTATION

registerAsProvider

#

Register as a service provider.

Arguments

  • input!
Returns!
MUTATION

reorderServiceQuestions

#

Reorder service questions.

Arguments

  • input!
ReturnsBoolean!
MUTATION

respondToInformationRequest

#

Answer one of a provider's questions.

Arguments

  • infoRequestId!
  • responseString!
Returns!
MUTATION

selectOffer

#

Select an offer for a service request. Select an offer for a service request. couponCode is optional and forgiving: a code that is invalid, expired, not applicable or under its minimum is ignored and the booking goes through at full price. Refusing the whole selection over a coupon would strand a customer who has already chosen and a provider who has already paid a credit to be chosen.

Arguments

  • offerId!
  • couponCodeString
Returns!
MUTATION

serviceMarketplacePing

#

Service health check mutation.

ReturnsString!
MUTATION

setDefaultPushProvider

#

Set a push provider as the default within its owner scope.

Arguments

  • idString!
ReturnsBoolean!
MUTATION

setMyProviderAvailability

#

Turn "Available to take jobs" on or off.

Arguments

  • onlineBoolean!
Returns!
MUTATION

setMyProviderServices

#

Replace the services you offer. Services you leave out are paused, not deleted, so their completed-job count and rating survive.

Arguments

  • input[!]!
Returns[!]!
MUTATION

skipRequest

#

"Not Interested" — dismiss a lead so it stops appearing in your list. Idempotent; call unskipRequest to bring it back.

Arguments

  • requestId!
  • reasonString
ReturnsBoolean!
MUTATION

startServiceJob

#

Provider marks the job as started. Records started_at on the accepted offer, which is what distinguishes a booked job from one under way.

Arguments

  • requestId!
Returns!
MUTATION

submitOffer

#

Submit an offer on a service request.

Arguments

  • input!
Returns!
MUTATION

suspendServiceProvider

#

Suspend a service provider.

Arguments

  • input!
Returns!
MUTATION

testPushProvider

#

Test a push provider. Without device_token it just verifies the credentials authenticate with Google; with a device_token it sends a real test notification to that device for an end-to-end check.

Arguments

  • idString!
  • deviceTokenString
Returns!
MUTATION

unlockRequest

#

Spend credits to see a lead's address, photos and contact details before quoting for it. Safe to call more than once — an already-open lead is not charged again.

Arguments

  • requestId!
Returns!
MUTATION

unskipRequest

#

Undo a dismissal.

Arguments

  • requestId!
ReturnsBoolean!
MUTATION

unsuspendServiceProvider

#

Unsuspend a service provider (set status back to Active).

Arguments

  • providerId!
Returns!
MUTATION

updateMyProviderProfile

#

Update provider profile.

Arguments

  • input!
Returns!
MUTATION

updatePushProvider

#

Update a push provider's name, credentials, or metadata.

Arguments

  • input!
Returns!
MUTATION

updateService

#

Update an existing service.

Arguments

  • input!
Returns!
MUTATION

updateServiceCategory

#

Update an existing service category.

Arguments

  • input!
Returns!
MUTATION

updateServiceCreditPackage

#

Arguments

  • input!
Returns!
MUTATION

updateServiceCreditSettings

#

Change the credit policy. Omitted fields keep their current value.

Arguments

  • input!
Returns!
MUTATION

updateServiceQuestion

#

Update an existing service question.

Arguments

  • input!
Returns!
MUTATION

withdrawOffer

#

Take back a bid the customer has not chosen yet. The credit it cost is **not** refunded: unlocking a lead reveals the address and contact details, and a refunded withdrawal would make "bid, read the lead, withdraw" a free way to browse the board. An accepted offer cannot be withdrawn — that is a booked job with an order behind it, and leaving one goes through cancellation.

Arguments

  • offerId!
Returns!