Inbound API calls to ServiceNow: synchronous and asynchronous design patterns<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } 1. Summary This article covers integrations where ServiceNow is the provider: an external system calls into the instance to read or write data. It does not cover ServiceNow acting as consumer, except where an outbound call is made from inside an inbound transaction, which is the single most damaging anti-pattern in this space and gets its own section. "Synchronous" and "asynchronous" get used loosely, and that looseness is where most bad designs come from. Separate two questions and the decisions become easy: Transport question. Does the caller's HTTP connection stay open until the work finishes? Processing question. Does the platform do the work on the request thread, or hand it to a queue and let a worker pick it up? An inbound call can be synchronous at the transport layer and still push work asynchronously (respond 201, then let a business rule fire later). It can also be asynchronous in the caller's mind but synchronous on the platform, which is how teams end up holding a thread for 200 seconds without realising it. NOTE: This is all based on a standard node configuration, all math must be updated to account for your specific instance's configuration, if you have questions of what that looks like please engage ServiceNow Support. 2. Inbound entry points and what each one is for Entry pointDefault processingUse it forDo not use it forTable API (/api/now/table/{table})Synchronous. Business rules, engines, and ACLs all run on the request thread.Reads. Simple, low volume writes to a table you control where the caller already knows the schema.Bulk loads. Any CMDB class. Anything needing validation or field mapping you do not want the caller to own.Import Set API, single record (/api/now/import/{staging})Synchronous transform. Returns the per row transform result including target sys_id and status.Real-time single record create or update where the caller needs the resulting record identifier in the same call.Arrays of records. Any transform that does slow work per row.Import Set API, multiple records (/api/now/import/{staging}/insertMultiple)Asynchronous transform by default. Returns import_set_id and multi_import_set_id only.Bulk and near-bulk writes. The default choice for volume.Cases where the caller must have per row results in the response, unless you accept a second call to fetch them.Scripted REST APIWhatever you write. Synchronous unless you deliberately defer.Contracts that need a custom request or response shape, custom status codes, or logic the staging table model cannot express.Anything a staging table plus a data policy would have handled. Every custom endpoint is permanent support surface.Batch API (/api/now/v1/batch)Synchronous, several sub-requests inside one HTTP call.Reducing round trips for a handful of small, fast, independent operations.Long running or large-result sub-requests. Batching does not reduce the work, it concentrates it into one transaction.Identification and Reconciliation, via Service Graph Connector or IntegrationHub ETL or IdentificationEngineDepends on the path chosen.Every CI write, without exception.Nothing. If the data is a CI, this is the path.Attachment APISynchronous.Binary content.Base64 blobs embedded in a JSON body. The general rule. Push data into a staging boundary you control, respond fast, and do the expensive work behind the response. Reach for a Scripted REST API when the contract genuinely cannot be expressed as a staging table, not as a first instinct. 3. What a synchronous inbound call actually costs A held HTTP connection is not free, and the cost lands on other users of the instance, not only on the integration. Four mechanisms matter. 3.1 Semaphores Concurrent transactions are governed by semaphore pools per node. Requests that cannot get a semaphore immediately go into a queue. Once the queue is full (150 positions in the documented behaviour), new requests are rejected immediately with HTTP 429. The important consequence: throughput is not a fixed requests-per-second number. It is a function of how long each request holds its semaphore. A 200 ms CRUD call and a 40 second import both consume one semaphore, and the second one blocks fifteen of the first ones behind it. Request design matters more than any RPM target you negotiate. 3.2 Session synchronisation If your client sends cookies back, the instance reuses the session. That sounds efficient and is often the documented recommendation, but reusing a session means one transaction at a time on that session. The integration serialises itself. The arithmetic from the platform performance guidance is worth memorising: [average request duration] x [requests per second] < 1 At 10 requests per second and 200 ms per request you are already at 2.0, which means you accumulate one second of backlog for every second of peak traffic. Once more than 5 requests are waiting on a session, or the running transaction exceeds 30 seconds, additional requests to that session are rejected with HTTP 202. Not reusing sessions removes the serialisation but creates a new session object per request. That is why the platform applies a short integration session timeout (5 minutes by default since Fuji Patch 7, overridable through the GetIntegrationSessionTimeout installation exit). Estimate your session count before you decide: [session timeout minutes] x [requests per second] x 60 / [node count] = [average active sessions per node] Heap is fixed per node, and a few thousand stale sessions is a measurable share of it. Either model works. Choosing one without doing this arithmetic does not. 3.3 Transaction quota rules Transaction Quota Rules (sysrule_quota) cancel transactions that exceed a configured duration. When an inbound REST call trips one, the caller receives: {"error":{"message":"Transaction cancelled: maximum execution time exceeded"},"status":"failure"} Two properties of these rules catch people out. All matching rules are evaluated, and the lowest limit wins, so the order field does not let you grant a higher ceiling by ordering a permissive rule first. And the rules are domain separated, which matters in service provider instances. The supported way to give one integration more headroom is to exclude that user from the out-of-box rule and create a dedicated rule for it, not to relax the global rule. Raising the quota is a stopgap. If a call needs 240 seconds, the design is wrong. 3.4 Payload ceilings LimitPropertyDefaultNotesInbound REST request bodyglide.rest.max_content_length10 MBMaximum supported value is 25 MB. Exceeding it returns Rejected large REST payload with content-length=....Inbound SOAP request bodyglide.soap.max_inbound_content_length20 MBRaising this aggressively has crashed instances. Treat 20 MB as the ceiling, not the starting point.Attachment sizecom.glide.attachment.max_sizeInstance dependent, commonly ~75 MBApplies to attachments generally, including outbound API bodies. Base64 inflates payloads by roughly a third. A 9 MB file does not fit in a 10 MB JSON body once encoded and wrapped. Use the Attachment API for binary content and keep the JSON body for metadata. 4. Choosing between synchronous and asynchronous Work down this list. The first row that produces a hard requirement decides the pattern. QuestionIf yesIf noMust the caller have the created record's number or sys_id before it can continue?Synchronous single record import, or Scripted RESTAsynchronousIs the caller a human waiting on a screen?Synchronous, and cap the work at a few hundred millisecondsAsynchronousIs the payload more than roughly 50 records?AsynchronousEitherDoes the transform need to call another system?Asynchronous, always, with the outbound call after commitEitherIs the caller able to poll or receive a callback?AsynchronousSynchronous, and reduce batch size until it fits the quotaDoes processing order matter across records?Synchronous, or asynchronous with explicit partitioningAsynchronousIs this a CI write?Route through IRE regardless of sync or asyncn/a The middle ground that usually wins. Accept synchronously, process asynchronously. The endpoint validates the payload, writes it to staging, returns 201 with a correlation identifier, and the transform runs behind it. The caller gets a fast, deterministic response and a way to find out what happened. 5. Import Set API behaviour you need to know before you design against it These are the behaviours that most commonly surprise teams in production. Single insert is synchronous, multi insert is not. POST /api/now/import/{staging} transforms on the request thread and returns a result array containing transform_map, table, display_value, record_link, status, and sys_id. POST /api/now/import/{staging}/insertMultiple returns only import_set_id and multi_import_set_id, because the rows have not been transformed yet when the response is generated. Forcing multi insert to be synchronous is possible and usually wrong. Create a record in REST Insert Multiples (sys_rest_insert_multiple), select the staging table, and set Transformation to synchronous. The transform then completes before the response returns, for the entire array. Even then the response body does not gain per row detail, so you have paid the full latency cost for very little. Multi insert maps on column label by default. This trips almost everyone. Single insert maps on column name. To make insertMultiple map on column name, add a Column Mapping child record on the sys_rest_insert_multiple record and set it to Column name. Labels change over time and column names do not, so this is worth configuring even if the labels currently happen to match. The import_attribute_name dictionary attribute is currently broken. It historically let you expose u_email to callers as email. It is reported non-functional in the Zurich and Australia families, with no workaround other than not using it (KB3032761). Build staging tables inside a scoped application so the columns have no u_ prefix, and you avoid needing the attribute at all. Asynchronous rows only move if the transformer job is running. If the Asynchronous Import Set Transformer scheduled job is deactivated, rows sit in the staging table indefinitely with no error anywhere obvious. Check this first when async rows appear stuck. Getting results back for an async load takes two more calls. There is no out-of-box endpoint that returns the outcome of an insertMultiple batch. The supported sequence is: GET /api/now/table/sys_import_set_run?sysparm_query=set={import_set_id} -> total, inserts, updates, processed, ignored, skipped, errors GET /api/now/table/sys_import_set_row_error?sysparm_query=run_history={run_sys_id} &sysparm_fields=row,error_code,error_message -> per row failure detail An efficient variant: always call the second query and treat an X-Total-Count of 0 as success, which saves a conditional round trip. Note that sys_-prefixed tables are not reachable through the REST API Explorer until they are added to glide.ui.permitted_tables, which is a UI restriction only and does not affect the API itself. Transform script variables shape the synchronous response. In a single insert, status_message and arbitrary properties on the response object set in an onComplete script are appended to the row result. error_message works in an onBefore script when combined with error = true to reject the row. These variables do not give you useful feedback on insertMultiple, which support has confirmed as a product gap rather than a configuration problem. HTTP 201 does not mean the target record exists. It means the staging row was created. Any caller that treats 201 as "incident created" will silently lose data the first time a transform rejects a row. State this explicitly in the interface contract. Roles and access. The calling account needs import_transformer, plus snc_platform_rest_api_access if strict REST API security is enabled (it should be). Without a specific write ACL on your staging table, anyone holding import_transformer can write to every staging table in the instance. Add a table-level write ACL scoped to the specific integration user. Data policies do validation without code. A data policy on the staging table making fields mandatory produces an automatic error response, with no onBefore script to maintain. 5.1 Worked example: the polling contract An insertMultiple batch does not tell you what happened to your rows in the response, so the outcome is retrieved in a second and third call. The first call returns only correlation identifiers. The second polls the transform run until it reports complete, then reads the aggregate counts. The third runs only when errors is non-zero and returns the per row failure detail. The set field on sys_import_set_run matches the import_set_id from the first response, and the run_history field on sys_import_set_row_error matches the sys_id of the run record from the second call, not the import set id. Watch the state field on the run record: it moves through loading and pending before complete, so poll with backoff rather than reading counts off the first response, which will show zeros. A multi_import_set_id is also returned when a large payload is split across several import sets; carry it on subsequent insertMultiple calls so the sets stay correlated, and query each set's run separately. One optimisation collapses the branch: always issue the third call and treat an X-Total-Count response header of 0 as success. That removes the conditional round trip at the cost of one always-on query. 6. Anti-patterns inside the import set pipeline 6.1 Outbound HTTP calls inside a synchronous transform This is the headline failure mode. A transform script calls RESTMessageV2.execute() (or SOAPMessageV2.execute()) to enrich a record, look up a code, or notify a downstream system, and it does so per row while the caller's HTTP connection is still open. execute() freezes the executing thread until the remote system responds. Every consequence follows from that one fact. What it does to the transaction. With 500 rows and a 400 ms remote response, the transform needs 200 seconds of wall clock time before it can return. A transaction quota rule will cancel it first, and the caller gets a 500 with no reliable statement of which rows committed before the cancel. Retrying replays the committed rows unless you built idempotency in. What it does to everyone else. The semaphore is held for the entire duration, including all the time spent waiting on a network you do not control. Under any real concurrency the semaphore queue fills and unrelated callers start receiving 429. Interactive users on the same node degrade alongside them. What it does to correctness. There is no retry boundary. If the remote call fails on row 300, rows 1 to 299 are committed in ServiceNow and possibly committed remotely too, with nothing durable to replay from. A remote outage becomes a data consistency incident rather than a queued backlog. What to do instead. Land the row, commit it, and let something with its own lifecycle make the call. Ranked from best to acceptable: Flow Designer or an async business rule on the target table, triggered after commit. The work has its own record, its own retry, and its own error state.The event queue. gs.eventQueue() in the transform, with a script action doing the outbound work. Cheapest possible cost inside the transform.executeAsync() instead of execute(). The thread is not blocked, but you cannot handle the response inline, which is fine when you do not need one.setEccTopic() through a MID Server. True asynchronous execution with a response you can process, at the cost of ECC queue latency, and the work executes outside the instance. If you are certain you need the remote value inside the transform (a required foreign key with no alternative source), that is a signal the design is wrong. Fetch the reference data on a schedule into a local lookup table and read it locally during the transform. 6.2 Leaving Run Business Rules checked when you do not need it On the transform map form, clearing Run Business Rules tells the platform to bypass business rules, engines, auditing, and update tracking on the target insert or update. The documented saving is 50 to 90 percent of import execution time. Before clearing it, confirm nothing on the target depends on those rules. If you need one or two, replicate that specific logic in a transform script and clear the option, rather than paying for the whole engine stack per row. Clearing the option does not bypass the sys_ field updates (sys_created_on, sys_updated_on, sys_mod_count, and the rest), so audit basics are preserved. 6.3 Unindexed GlideRecord lookups in per row scripts An onBefore script that queries a large table on an unindexed column runs once per row. At 100,000 rows that is 100,000 slow queries, and repeatedly pulling large unindexed result sets flushes the database buffer pool, which degrades the entire instance rather than just the import. Index the column, or restructure the lookup, or move the enrichment to an asynchronous step. 6.4 Missing or non-unique coalesce Without a coalesce field, every staged row inserts a new target record. With a non-unique or unindexed coalesce field, every row triggers an expensive match query. Choose a coalesce field that is unique, stable, and indexed: an employee identifier, a serial number, a source native key. Not a display name. 6.5 Concurrency without a coalesce field Running transforms in parallel with no coalesce is a duplicate generator, because two workers can process rows for the same logical entity at the same time. If parallelism is required and coalescing is not possible, partition the source data so that all rows for one entity land in one partition. 6.6 Doing batch work in onAfter instead of onComplete onAfter runs once per row. onComplete runs once at the end of the transform. Summary logging, notifications, and reconciliation counts belong in onComplete. Putting them in onAfter multiplies the cost by the row count for no benefit. 6.7 Writing CI data straight to a CMDB class Identification and reconciliation is only applied when the data arrives through a path that invokes it: ServiceNow Discovery, a Service Graph Connector, IntegrationHub ETL, or an explicit IdentificationEngine call. A plain transform map to cmdb_ci_server, or a Table API POST to the same, bypasses all of it. What you lose: identification rules that find the existing CI, sys_object_source native key tracking, data source precedence that stops a weak source overwriting an authoritative one, automatic model creation, and correct relationship handling. The load reports success while the CMDB quietly accumulates duplicates. If no certified connector exists for the source, write a Scripted REST resource that builds an IRE payload and calls the engine, rather than writing to the class directly. 6.8 Import Set Deleter disabled or retention too long Staging tables extend sys_import_set_row and are cleaned by the Import Set Deleter scheduled job, default retention 7 days. Two failure modes: Job disabled. Staging tables grow without bound. Inserts get progressively slower and eventually imports stop. Recovery usually requires ServiceNow support to truncate before the job can be safely re-enabled.Retention too long for the volume. Nightly multi-million row imports keep the staging table in the multi-million row range permanently even with the job running. Reducing retention to 2 or 3 days is a common and effective fix. 6.9 Letting the payload change the schema at runtime Importing a column that does not exist on the staging table triggers a schema change, and the entire table is locked for the duration, which can be 5 to 10 minutes on a large table. Nothing can select or insert during that window. Define staging columns explicitly and reject unknown fields rather than letting a caller's payload alter your schema. 6.10 Leaving text indexing on during a large initial load For initial loads above roughly 500,000 records, each target insert queues a text_index event in sysevent. Flooding sysevent slows inserts progressively (measured at 5 to 6 seconds per row in the worst cases) and delays every other event on the instance, so notifications stop arriving and metrics stall. Turn text indexing off on the target table for the load, re-enable it afterwards, and run a re-index for the affected tables. Use the opportunity to add no_text_index=true to fields that never need to be searchable, which is worth doing permanently on CMDB and sys_user tables. 6.11 One shared integration account for every integration A single sn.integration.user is convenient until something goes wrong. Separate accounts per integration give you per integration rate limits (limits are counted per user), attributable logs, and the ability to lock out one misbehaving integration without stopping the rest. Use local accounts, not externally authenticated ones, because remote authentication adds real overhead to every request. 6.12 No idempotency key Callers retry. Networks time out after the work committed. Without a stable external identifier carried in the payload and used as the coalesce field, every retry is a duplicate. This is cheap to design in and expensive to retrofit. 6.13 Base64 attachments inside the JSON body Attachments encoded into a JSON payload hit glide.rest.max_content_length quickly and hold the request thread while the platform parses a very large string. Create the record first, then attach through the Attachment API with a separate call. 7. The read side Most inbound load in a mature instance is reads, and most read problems are the same three problems. Do not page with a growing offset. sysparm_offset makes the database read and discard every preceding row, so page 500 costs far more than page 1. Page from a watermark on an indexed column instead: sysparm_query=sys_updated_on>{last_seen}^ORDERBYsys_updated_on &sysparm_fields=sys_id,number,short_description,state,sys_updated_on &sysparm_exclude_reference_link=true &sysparm_limit=1000 Store the last value returned and use it as the next page's watermark. Page time stays flat as the table grows. If you also need delete detection, run a periodic reconciliation on identifiers rather than trying to express it in the delta query. The watermark is not safe as written. sys_updated_on has one second resolution, so any number of records can share the boundary second. If a page ends in the middle of that second, a strict sys_updated_on>{watermark} silently skips every remaining record in it, and switching to >= reprocesses the head of the second on every run. The first is data loss you will not notice until an audit; the second is duplicate work, and duplicate rows if the target has no coalesce field. Two fixes, either of which is sufficient. The first is a compound keyset: order by sys_updated_on then sys_id, and express the boundary as "past this second, or in this second past the last id already seen". sysparm_query=sys_updated_on>{ts}^NQsys_updated_on={ts}^sys_id>{last_sys_id}^ORDERBYsys_updated_on^ORDERBYsys_id The ^NQ is an OR. This never skips and never repeats, at the cost of a query the optimiser handles less cleanly than a single range predicate. The simpler alternative is overlap and dedupe: page with >= and re-read a few seconds of overlap each run, then discard already seen sys_id values on your side. Robust as long as downstream is idempotent, which it should already be. Two rules apply to both. Never chase the leading edge: leave a guard band of a few seconds so you do not read a second that is still being written, and never advance the watermark into the current second. And store the watermark from the maximum sys_updated_on you actually read, not from the clock at query time, so minor node clock skew across the cluster cannot make you skip records committed while the query was running. Ask for what you need and nothing else. sysparm_fields reduces both database and serialisation work. sysparm_exclude_reference_link=true removes a link object from every reference field on every row. sysparm_display_value=true forces the server to resolve display values for every reference on every row, which is expensive at volume. Use raw values for machine-to-machine sync and resolve labels once on your side. Sizing. 500 to 2,000 rows per page is a reasonable starting band, tuned against observed response times. sysparm_limit is not optional on a large table. Counts. Use the Aggregate API. Never page a table to count it. Handle 429 properly. Exponential backoff, and honour the Retry-After response header rather than retrying at a fixed interval. A tight retry loop against a saturated node is indistinguishable from an attack. 8. Guardrails to configure on the provider side These are provider-side controls. Configure them before go-live, not after the first incident. ControlWhereWhyRate limit rulesSystem Web Services > REST > Rate Limit RulesCaps requests per hour per user or role. Returns 429 with Retry-After and X-RateLimit-* headers when a rule exists. Set one per integration user and per endpoint.Transaction quota rulesSystem Definition > Transaction Quota RulesBounds worst case transaction duration. Create a dedicated rule for an integration user rather than loosening the global rule.Write ACL on each staging tableTable ACLWithout one, any holder of import_transformer can write to every staging table in the instance.Strict REST API securitysnc_platform_rest_api_access roleRequires explicit grant of platform REST access rather than inheriting it.Dedicated local integration user per integrationsys_userAttribution, isolation, per user rate limiting, emergency lockout.Data policies on staging tablesData policyDeclarative mandatory-field validation with an automatic error response.Rate limit violationsSystem Web Services > REST > Rate Limit ViolationsShows which rules are being hit and by whom.API analytics and the inbound API integration usage dashboardREST and SOAP usage reportingBaseline volume and duration by endpoint and user, so you can tell a change in behaviour from a change in volume. 9. Reference patterns Pattern A: real-time single record create Caller needs the resulting number immediately. POST /api/now/import/x_acme_incident_stage { "external_id": "SRV-84210", "short_description": "...", "impact": "2" } 201 { "import_set": "ISET0010572", "result": [ { "table": "incident", "display_value": "INC0011139", "status": "inserted", "sys_id": "a9cd...", "status_message": "Imported" } ] } Design rules: external_id is the coalesce field. Validation lives in a data policy plus an onBefore script that sets error_message on rejection. No outbound calls anywhere in the transform. Rate limit rule sized to the caller's real peak. Pattern B: high volume synchronisation Caller has thousands of records and can poll. Caller batches into arrays sized to stay well inside glide.rest.max_content_length, with a stable external key on every row.POST /api/now/import/{staging}/insertMultiple, passing multi_import_set_id from the first response on subsequent calls so the batch stays correlated.Instance returns 201 immediately. Transform runs behind it.Caller polls sys_import_set_run on the returned import_set_id, then sys_import_set_row_error if errors is non-zero.Run Business Rules cleared on the transform map. Enrichment runs in an async business rule after commit. For scheduled bulk imports rather than pushed ones, enable Concurrent Import on the Scheduled Data Import record. It splits the set across parallel transform jobs and substantially reduces run time on large loads. It does not preserve processing order unless you partition the source deliberately. Pattern C: CMDB ingestion Certified Service Graph Connector if one exists for the source. IntegrationHub ETL for manual and one-off bulk loads. A Scripted REST resource that constructs an IRE payload and calls IdentificationEngine when neither is available. Never a direct write to a CI class. Pattern D: notifying an external system after ingest Transform commits the record and does nothing else. An async business rule or Flow on the target table makes the outbound call, with its own error handling and retry. The inbound caller's latency is unaffected by the downstream system's availability. 10. Pre-go-live checklist Design Synchronous or asynchronous chosen deliberately, with the reason recordedCaller contract states that 201 means "accepted", not "target record created", where that is trueIdempotency key defined and used as the coalesce fieldNo outbound HTTP call anywhere in a transform scriptCI data routed through IRE Configuration Dedicated local integration user, roles scoped to what it actually needsWrite ACL on the staging table restricted to that userRate limit rule created and sizedTransaction quota headroom verified for the worst case payload, via a dedicated rule if neededRun Business Rules cleared, or the dependency on it documentedCoalesce field is unique, stable, and indexedAsynchronous Import Set Transformer job confirmed activeImport Set Deleter active, retention set appropriately for the volumeColumn mapping set to Column name if insertMultiple is used Validation Full-volume import run in a sub-production instance with production-like dataPer row transform duration measured, not estimatedText indexing strategy decided for the initial loadError path tested: what does the caller see when a row is rejected, when the quota trips, when a 429 is returnedRetry behaviour tested, including a retry after a partial commitMonitoring in place on rate limit violations and API usage Appendix A: interpreting inbound responses ResponseUsual meaningFirst thing to check201 with import_set_id onlyinsertMultiple accepted, transform not yet runWhether the caller is incorrectly treating this as target-record confirmation201 with a populated result arraySingle insert, transform completeThe status value per row: inserted, updated, ignored, error202Session synchronisation rejection: too many waiters on one session, or a transaction over 30 secondsWhether the client is reusing cookies and serialising itself429Rate limit rule exceeded, or the semaphore queue is fullRate Limit Violations, then transaction duration on that endpoint500 with "Transaction cancelled: maximum execution time exceeded"Transaction quota rule cancelled the requestTransform duration and any blocking outbound call in the script pathRejected large REST payload with content-length=...Body exceeded glide.rest.max_content_lengthWhether attachments are being sent inline as base64Rows stuck in the staging table with no errorAsync transform never ranWhether the Asynchronous Import Set Transformer job is active Appendix B: tables and properties quick reference Tables TablePurposesys_import_set_rowBase class for all staging tablessys_import_setThe import set headersys_import_set_runTransform history: total, inserts, updates, processed, ignored, skipped, errorssys_import_set_row_errorPer row transform failuressys_rest_insert_multipleControls insertMultiple behaviour: sync or async, column mapping modesys_transform_map / sys_transform_entryTransform map and its field mapssysrule_quotaTransaction quota rulessys_object_sourceSource native key tracking for CIs Properties PropertyPurposeglide.rest.max_content_lengthInbound REST request body ceiling, 10 MB default, 25 MB maximumglide.soap.max_inbound_content_lengthInbound SOAP body ceiling, 20 MB defaultcom.glide.attachment.max_sizeAttachment size ceilingglide.ui.session_timeoutGlobal session timeout, 30 minutes baseglide.ui.permitted_tablesAdds sys_ tables to the REST API Explorer, UI only